import { useState } from "react";
import { Combobox } from "@cloudflare/kumo";
// Basic demo with TriggerInput
export function ComboboxDemo() {
const [value, setValue] = useState<string | null>("Apple");
return (
<Combobox
value={value}
onValueChange={(v) => setValue(v as string | null)}
items={fruits}
>
<Combobox.TriggerInput placeholder="Please select" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{(item: string) => (
<Combobox.Item key={item} value={item}>
{item}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
);
} Installation
Barrel
import { Combobox } from "@cloudflare/kumo"; Granular
import { Combobox } from "@cloudflare/kumo/components/combobox"; Usage
import { useState } from "react";
import { Combobox } from "@cloudflare/kumo";
const fruits = ["Apple", "Banana", "Cherry", "Date", "Elderberry"];
export default function Example() {
const [value, setValue] = useState<string | null>(null);
return (
<Combobox value={value} onValueChange={setValue} items={fruits}>
<Combobox.TriggerInput placeholder="Select a fruit" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{(item) => (
<Combobox.Item key={item} value={item}>
{item}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
);
} Examples
Sizes
The Combobox supports four size variants that match the Input component: xs,
sm, base (default), and lg.
import { useState } from "react";
import { Combobox } from "@cloudflare/kumo";
/** Demonstrates the different size variants: xs, sm, base, and lg. */
export function ComboboxSizesDemo() {
const [smValue, setSmValue] = useState<string | null>(null);
const [baseValue, setBaseValue] = useState<string | null>(null);
return (
<div className="flex flex-wrap items-center gap-4">
<Combobox
size="sm"
value={smValue}
onValueChange={(v) => setSmValue(v as string | null)}
items={fruits.slice(0, 8)}
>
<Combobox.TriggerInput placeholder="Small (sm)" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{(item: string) => (
<Combobox.Item key={item} value={item}>
{item}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
<Combobox
size="base"
value={baseValue}
onValueChange={(v) => setBaseValue(v as string | null)}
items={fruits.slice(0, 8)}
>
<Combobox.TriggerInput placeholder="Base (default)" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{(item: string) => (
<Combobox.Item key={item} value={item}>
{item}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
</div>
);
} Size also applies to TriggerValue (searchable inside variant):
import { useState } from "react";
import { Combobox } from "@cloudflare/kumo";
/** Demonstrates size variants with TriggerValue (searchable inside). */
export function ComboboxSizesSearchableInsideDemo() {
const [smValue, setSmValue] = useState<Language>(languages[0]);
const [baseValue, setBaseValue] = useState<Language>(languages[1]);
return (
<div className="flex flex-wrap items-center gap-4">
<Combobox
size="sm"
value={smValue}
onValueChange={(v) => setSmValue(v as Language)}
items={languages}
>
<Combobox.TriggerValue className="w-[160px]" />
<Combobox.Content>
<Combobox.Input placeholder="Search" />
<Combobox.Empty />
<Combobox.List>
{(item: Language) => (
<Combobox.Item key={item.value} value={item}>
{item.emoji} {item.label}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
<Combobox
size="base"
value={baseValue}
onValueChange={(v) => setBaseValue(v as Language)}
items={languages}
>
<Combobox.TriggerValue className="w-[180px]" />
<Combobox.Content>
<Combobox.Input placeholder="Search" />
<Combobox.Empty />
<Combobox.List>
{(item: Language) => (
<Combobox.Item key={item.value} value={item}>
{item.emoji} {item.label}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
</div>
);
} Searchable Item (Inside)
A searchable select component inside popup that allows users to filter and select.
import { useState } from "react";
import { Combobox } from "@cloudflare/kumo";
// Searchable inside popup with TriggerValue
export function ComboboxSearchableInsideDemo() {
const [value, setValue] = useState<Language>(languages[0]);
return (
<Combobox
value={value}
onValueChange={(v) => setValue(v as Language)}
items={languages}
>
<Combobox.TriggerValue className="w-[200px]" />
<Combobox.Content>
<Combobox.Input placeholder="Search languages" />
<Combobox.Empty />
<Combobox.List>
{(item: Language) => (
<Combobox.Item key={item.value} value={item}>
{item.emoji} {item.label}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
);
} Grouped
Group items into categories using the Group and GroupLabel components.
import { useState } from "react";
import { Combobox } from "@cloudflare/kumo";
// Grouped items demo
export function ComboboxGroupedDemo() {
const [value, setValue] = useState<ServerLocation | null>(null);
return (
<Combobox
value={value}
onValueChange={(v) => setValue(v as ServerLocation | null)}
items={servers}
>
<Combobox.TriggerInput
className="w-[200px]"
placeholder="Select server"
/>
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{(group: ServerLocationGroup) => (
<Combobox.Group key={group.value} items={group.items}>
<Combobox.GroupLabel>{group.value}</Combobox.GroupLabel>
<Combobox.Collection>
{(item: ServerLocation) => (
<Combobox.Item key={item.value} value={item}>
{item.label}
</Combobox.Item>
)}
</Combobox.Collection>
</Combobox.Group>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
);
} Multiple
Allow users to select multiple options from the list.
import { useState } from "react";
import { Combobox, Text, Button } from "@cloudflare/kumo";
export function ComboboxMultipleDemo() {
const [value, setValue] = useState<BotItem[]>([]);
return (
<div className="flex gap-2">
<Combobox
value={value}
onValueChange={setValue}
items={bots}
isItemEqualToValue={(bot: BotItem, selected: BotItem) =>
bot.value === selected.value
}
multiple
>
<Combobox.TriggerMultipleWithInput
className="w-[400px]"
placeholder="Select bots"
renderItem={(selected: BotItem) => (
<Combobox.Chip key={selected.value}>{selected.label}</Combobox.Chip>
)}
inputSide="right"
/>
<Combobox.Content className="max-h-[200px] min-w-auto overflow-y-auto">
<Combobox.Empty />
<Combobox.List>
{(item: BotItem) => (
<Combobox.Item key={item.value} value={item}>
<div className="flex gap-2">
<Text>{item.label}</Text>
<Text variant="secondary">{item.author}</Text>
</div>
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
<Button variant="primary">Submit</Button>
</div>
);
} With Field
Add label and description using the built-in Field wrapper.
Select your preferred database
import { useState } from "react";
import { Combobox } from "@cloudflare/kumo";
export function ComboboxWithFieldDemo() {
const [value, setValue] = useState<DatabaseItem | null>(null);
return (
<div className="w-80">
<Combobox
items={databases}
value={value}
onValueChange={setValue}
label="Database"
description="Select your preferred database"
>
<Combobox.TriggerInput placeholder="Select database" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{(item: DatabaseItem) => (
<Combobox.Item key={item.value} value={item}>
{item.label}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
</div>
);
} Disabled
Pass the disabled prop to prevent interaction. Works with both TriggerInput and TriggerValue.
import { Combobox } from "@cloudflare/kumo";
export function ComboboxDisabledDemo() {
return (
<div className="flex flex-wrap gap-4 items-start">
<Combobox value="Apple" items={fruits} disabled>
<Combobox.TriggerInput
className="w-[200px]"
placeholder="Select fruit"
/>
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{(item: string) => (
<Combobox.Item key={item} value={item}>
{item}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
<Combobox value={languages[0]} items={languages} disabled>
<Combobox.TriggerValue className="w-[200px]" />
<Combobox.Content>
<Combobox.Input placeholder="Search" />
<Combobox.Empty />
<Combobox.List>
{(item: Language) => (
<Combobox.Item key={item.value} value={item}>
{item.emoji} {item.label}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
</div>
);
} Error State
Display validation errors with the error prop.
import { useState } from "react";
import { Combobox } from "@cloudflare/kumo";
export function ComboboxErrorDemo() {
const [value, setValue] = useState<DatabaseItem | null>(null);
return (
<div className="w-80">
<Combobox
items={databases}
value={value}
onValueChange={setValue}
label="Database"
error={{ message: "Please select a database", match: true }}
>
<Combobox.TriggerInput placeholder="Select database" />
<Combobox.Content>
<Combobox.Empty />
<Combobox.List>
{(item: DatabaseItem) => (
<Combobox.Item key={item.value} value={item}>
{item.label}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Content>
</Combobox>
</div>
);
} Customizing Dropdown Height
By default, Combobox.Content has a max height of 24rem (384px) or the available viewport space, whichever is smaller. The dropdown scrolls automatically when content exceeds this height.
To customize the max height, pass a className to Combobox.Content:
// Shorter dropdown (200px)
<Combobox.Content className="max-h-[200px]">
// Taller dropdown (500px)
<Combobox.Content className="max-h-[500px]">
// Use Tailwind presets
<Combobox.Content className="max-h-64"> // 256px
<Combobox.Content className="max-h-96"> // 384px (same as default) API Reference
Combobox
Root component for the searchable select.
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "xs" | "sm" | "base" | "lg" | "base" | Size of the combobox trigger. Matches Input component sizes. - `"xs"` — Extra small for compact UIs (h-5 / 20px) - `"sm"` — Small for secondary fields (h-6.5 / 26px) - `"base"` — Default size (h-9 / 36px) - `"lg"` — Large for prominent fields (h-10 / 40px) |
| inputSide | "right" | "top" | "right" | Position of the text input relative to chips in multi-select mode. - `"right"` — Input inline to the right of chips - `"top"` — Input above chips |
| items* | T[] | - | Array of items to display in the dropdown |
| value | T | T[] | - | Currently selected value(s) |
| children | ReactNode | - | Combobox content (trigger, content, items) |
| className | string | - | Additional CSS classes |
| label | ReactNode | - | Label content for the combobox (enables Field wrapper) - can be a string or any React node |
| required | boolean | - | Whether the combobox is required |
| labelTooltip | ReactNode | - | Tooltip content to display next to the label via an info icon |
| description | ReactNode | - | Helper text displayed below the combobox |
| error | string | object | - | Error message or validation error object |
| onValueChange | (value: T | T[]) => void | - | Callback when selection changes |
| multiple | boolean | - | Allow multiple selections |
| isItemEqualToValue | (item: T, value: T) => boolean | - | Custom equality function for comparing items |
Combobox.Content
Dropdown container for the list.
| Prop | Type | Default |
|---|---|---|
| className | string | - |
| align | ComboboxBase.Positioner.Props["align"] | - |
| alignOffset | ComboboxBase.Positioner.Props["alignOffset"] | - |
| side | ComboboxBase.Positioner.Props["side"] | - |
| sideOffset | ComboboxBase.Positioner.Props["sideOffset"] | - |
Combobox.Item
Individual selectable option.
| Prop | Type | Default |
|---|
No component-specific props. Accepts standard HTML attributes.
Additional Sub-components
Combobox.TriggerInput- Single-select input triggerCombobox.TriggerValue- Button trigger showing selected valueCombobox.TriggerMultipleWithInput- Multi-select with chipsCombobox.Input- Search input inside dropdownCombobox.List- List container with render propCombobox.Group- Group container for categorized itemsCombobox.GroupLabel- Header label for a groupCombobox.Collection- Items container within a groupCombobox.Chip- Selected item chipCombobox.Empty- Empty state message