Basic Usage
A simple select component. Use the items prop
to provide options — when items is provided without
children, the select automatically renders options. This also controls what text appears in the
trigger when an option is selected. For custom option rendering, you can still provide manual
Select.Option children.
import { Select } from "@cloudflare/kumo";
function App() {
const [value, setValue] = useState("apple");
return (
<Select
className="w-[200px]"
value={value}
onValueChange={(v) => setValue(v ?? "apple")}
items={{ apple: "Apple", banana: "Banana", cherry: "Cherry" }}
/>
)
} Basic Label and Value A select component where the internal value differs from the displayed text. Useful when you need to store identifiers while showing user-friendly labels.
import { Select } from "@cloudflare/kumo";
// Option 1: Using object map
function App() {
const [value, setValue] = useState("bug");
return (
<Select
className="w-[200px]"
value={value}
onValueChange={(v) => setValue(v ?? "bug")}
items={{
bug: "Bug",
documentation: "Documentation",
feature: "Feature",
}}
/>
)
}
// Option 2: Using array of objects
function App() {
const [value, setValue] = useState("bug");
return (
<Select
className="w-[200px]"
value={value}
onValueChange={(v) => setValue(v as any)}
items={[
{value: "bug", label: "Bug"},
{value: "documentation", label: "Documentation"},
{value: "feature", label: "Feature"},
]}
/>
)
} Placeholder A select component with a placeholder option. The empty value must be null, not an empty string or undefined. Using undefined will cause the component to behave as uncontrolled.
import { Select } from "@cloudflare/kumo";
function App() {
const [value, setValue] = useState<string | null>(null);
return (
<Select
className="w-[200px]"
value={value}
onValueChange={(v) => setValue(v as any)}
items={[
// Placeholder
{ value: null, label: "Please select" },
{ value: "bug", label: "Bug" },
{ value: "documentation", label: "Documentation" },
{ value: "feature", label: "Feature" },
]}
/>
)
} Custom Rendering A select component that demonstrates custom rendering capabilities for both the trigger button and dropdown options, allowing you to work with complex object data structures instead of simple string values.
const languages = [
{ value: "en", label: "English", emoji: "🇬🇧" },
{ value: "fr", label: "French", emoji: "🇫🇷" },
{ value: "de", label: "German", emoji: "🇩🇪" },
{ value: "es", label: "Spanish", emoji: "🇪🇸" },
{ value: "it", label: "Italian", emoji: "🇮🇹" },
{ value: "pt", label: "Portuguese", emoji: "🇵🇹" },
];
function App() {
const [value, setValue] = useState(languages[0]);
return (
<Select
className="w-[200px]"
renderValue={(v) => (
<span>
{v.emoji} {v.label}
</span>
)}
value={value}
onValueChange={(v) => setValue(v as any)}
>
{languages.map((language) => (
<Select.Option key={language.value} value={language}>
{language.emoji} {language.label}
</Select.Option>
))}
</Select>
);
} Select compares value with items to find which one is selected. For object items, it will compare if the object is the same reference not by value by default. If you want to compare object items by value, you can use isItemEqualToValue prop.
function App() {
const languages = [
{ value: "en", label: "English", emoji: "🇬🇧" },
{ value: "fr", label: "French", emoji: "🇫🇷" },
// ...
];
const [value, setValue] = useState(languages[0]);
return (
<Select
className="w-[200px]"
renderValue={(v) => (
<span>
{v.emoji} {v.label}
</span>
)}
value={value}
onValueChange={(v) => setValue(v as any)}
// Provides custom comparison logic
isItemEqualToValue={(item, value) => item.value === value.value}
>
{languages.map((language) => (
<Select.Option key={language.value} value={language}>
{language.emoji} {language.label}
</Select.Option>
))}
</Select>
);
} Loading
A select component with loading state. The loading state is passed to
the component via the loading prop.
Loading State
Loading From Server (simulated 2s delay)
function App() {
// NULL is for unselected value
const [value, setValue] = useState(null);
const { data, isLoading } = useQuery({...});
return (
<Select value={value} onValueChange={(v) => setValue(v as any)} loading={isLoading}>
{data?.map((item) => (
<Select.Option key={item.id} value={item.id}>
{item.name}
</Select.Option>
))}
</Select>
);
} Multiple Item A select component with multiple selection enabled. The value is an array of selected items.
function App() {
const [value, setValue] = useState<string[]>(["Name", "Location", "Size"]);
return (
<Select
className="w-[250px]"
multiple
renderValue={(value) => {
if (value.length > 3) {
return (
<span className="line-clamp-1">
{value.slice(2).join(", ") + ` and ${value.length - 2} more`}
</span>
);
}
return <span>{value.join(", ")}</span>;
}}
value={value}
onValueChange={(v) => setValue(v as any)}
>
<Select.Option value="Name">Name</Select.Option>
<Select.Option value="Location">Location</Select.Option>
<Select.Option value="Size">Size</Select.Option>
<Select.Option value="Read">Read</Select.Option>
<Select.Option value="Write">Write</Select.Option>
<Select.Option value="CreatedAt">Created At</Select.Option>
</Select>
);
} More Example
const authors = [
{ id: 1, name: "John Doe", title: "Programmer" },
{ id: 2, name: "Alice Smith", title: "Software Engineer" },
{ id: 3, name: "Michael Chan", title: "UI/UX Designer" },
{ id: 4, name: "Sok Dara", title: "DevOps Engineer" },
{ id: 5, name: "Emily Johnson", title: "Product Manager" },
{ id: 6, name: "Visal In", title: "System Engineer" },
{ id: 7, name: "Laura Kim", title: "Technical Writer" },
];
function App() {
const [value, setValue] = useState<(typeof authors)[0] | null>(null);
return (
<Select
className="w-[200px]"
onValueChange={(v) => setValue(v as any)}
value={value}
isItemEqualToValue={(item, value) => item?.id === value?.id}
renderValue={(author) => {
return author?.name ?? "Please select author";
}}
>
{authors.map((author) => (
<Select.Option key={author.id} value={author}>
<div className="flex items-center gap-2 w-[300px] justify-between">
<Text>{author.name}</Text>
<Text variant="secondary">{author.title}</Text>
</div>
</Select.Option>
))}
</Select>
)
} API Reference
Select
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Additional CSS classes merged via `cn()`. |
| label | ReactNode | - | Label content for the select (enables Field wrapper) — can be a string or any React node. |
| hideLabel | boolean | - | Visually hide the label while keeping it accessible to screen readers. Set to `false` to show a visible label above the select via the Field wrapper. |
| placeholder | string | - | Placeholder text shown when no value is selected. |
| loading | boolean | - | When `true`, shows a skeleton loader in place of the selected value. |
| disabled | boolean | - | Whether the select is disabled. |
| required | boolean | - | Whether the select is required. When `false`, shows "(optional)" text. |
| labelTooltip | ReactNode | - | Tooltip content displayed next to the label via an info icon. |
| value | string | - | Currently selected value (controlled mode). |
| children | ReactNode | - | `Select.Option` elements to render in the dropdown. |
| description | ReactNode | - | Helper text displayed below the select. |
| error | string | object | - | Error message string or validation error object with `match` key. |
| onValueChange | (value: string) => void | - | Callback when selection changes |
| defaultValue | string | - | Initial value for uncontrolled mode |
Select.Option
| Prop | Type | Default |
|---|
No component-specific props. Accepts standard HTML attributes.