Combobox
Filterable single-select with an animated custom listbox, built on the dropdown open/close machine
Category: inputs · WCAG 2.2-AA · select, combobox, dropdown, filter, search
Sizes
- sm
- md
- lg
States
- closed
- open
- error
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | — | Base id for the input and its listbox/aria wiring; auto-generated when omitted. |
options | ComboboxOption[] | — | The selectable options. |
value | string | — | The controlled value. |
defaultValue | string | — | The initial value when uncontrolled. |
onValueChange | (value: string | undefined) => void | — | Called with the selected option value (or undefined when cleared). |
clearable | boolean | false | When true, shows a control to clear the selected value. |
searchable | boolean | true | When true the field is a text input that filters the list as the user types (the APG editable combobox). When false it is a button that opens the list, and type-to-select jumps to a matching option (the APG select-only combobox). |
loading | boolean | false | When true, the list reports itself as busy and shows a loading row instead of the empty message. |
onSearchChange | (query: string) => void | — | Called with the search text on every keystroke. Pair it with filter={() => true} for a server-driven list. |
filter | (option: ComboboxOption, query: string) => boolean | — | Replaces the built-in diacritic-insensitive matcher. |
creatable | boolean | false | When true, offers the current search text as a new option. |
onCreate | (label: string) => void | — | Called with the typed label when the user picks the "create" row. |
name | string | — | Submitted with a surrounding form — a hidden input carrying the selected value. |
required | boolean | — | Marks the control as required for assistive technology. |
open | boolean | — | Controlled open state of the listbox. |
defaultOpen | boolean | false | The initial open state when uncontrolled. |
onOpenChange | (open: boolean) => void | — | Called when the listbox opens or closes. |
label | string | — | Text label for the control. |
hint | string | — | Supplementary hint text shown with the control. |
error | string | — | Error message shown when the value is invalid. |
size | 'sm' | 'md' | 'lg' | 'md' | Visual size of the component (e.g. 'sm', 'md', 'lg'). |
disabled | boolean | false | When true, disables the control and removes it from the tab order. |
labels | ComboboxLabels | — | Overrides for the component’s user-visible strings (i18n). |
className | string | — | Additional CSS class names merged onto the root element. |
aria-labelledby | string | — | Wired automatically by a wrapping `Field` — its label id, forwarded to the focusable control so the Field's label names it. |
aria-describedby | string | — | Wired automatically by a wrapping `Field` — the ids of its hint/error text, forwarded to the focusable control so the supporting text is announced. |
aria-invalid | boolean | — | Wired automatically by a wrapping `Field` when it is in an error state. |
ariaLabel | string | — | Invisible accessible name, for when a visible element outside this component already labels it and `label` would render that text a second time. ⚠ `label` on this component is **visible**; `IconButton.label`/`Sparkline.label` are invisible names, which is the prior that costs adopters a duplicated label. The raw DOM `aria-label` still wins over this. |
Design tokens
--cascivo-color-accent-text--cascivo-color-surface--cascivo-color-surface-overlay--cascivo-color-bg-subtle--cascivo-color-border--cascivo-color-border-strong--cascivo-color-text--cascivo-color-text-muted--cascivo-color-text-subtle--cascivo-color-accent--cascivo-color-destructive--cascivo-font-medium--cascivo-font-bold--cascivo-radius-field--cascivo-radius-overlay--cascivo-radius-item--cascivo-radius-control--cascivo-shadow-overlay--cascivo-motion-enter--cascivo-z-dropdown--cascivo-target-min-coarse
When to use
- Single-select from a long list where type-to-filter makes finding an option faster
- Form fields where the value is one of many known options (country, assignee, repository)
When not to use
- Short option lists (≈2–7) where filtering adds no value — use Select or SegmentedControl
- Selecting multiple values — use MultiSelect
- Triggering actions or commands — use Dropdown or CommandMenu
How to build an accessible Combobox in React →
Examples
Grouped options
Options carrying a group render under a labelled role="group" heading.
<Combobox options={[{value:'de',label:'Germany',group:'Europe'},{value:'jp',label:'Japan',group:'Asia'}]} />Remote search
filter={() => true} hands filtering to the server; loading marks the list busy between keystroke and response.
<Combobox options={results} loading={pending} onSearchChange={search} filter={() => true} />Select-only
A button rather than a text field; printable characters type-to-select, and Home/End jump the list.
<Combobox options={options} searchable={false} />Basic combobox
<Combobox
label="Country"
options={[
{ value: 'us', label: 'United States' },
{ value: 'de', label: 'Germany' },
{ value: 'fr', label: 'France' },
]}
onValueChange={(value) => console.log(value)}
/>Related components
- Select — Use for short lists that do not need type-to-filter
- MultiSelect — Use when more than one value can be selected
- CommandMenu — Use the Cmd+K palette for command/navigation search rather than value selection