Forms & input
Combobox
A searchable single-select for when a plain dropdown is too long — assignees, projects, labels. Type to filter, navigate with the keyboard, and clear in a click. Built on Base UI for filtering, keyboard navigation, and ARIA combobox semantics.
npx shadcn@latest add @orbit/comboboxOverview
Click to open, type to filter, ↑↓ to move, Enter to pick.
Datasets
The same component adapts its option rows to the data — avatars for people, icons for projects, colour dots for labels. Pass any ReactNode as an item's lead and a secondary sub line.
Preselected & controlled
Pass defaultValue for an uncontrolled initial selection, or value + onValueChange to control it. Clearing calls onValueChange(null).
value: "priya"
Sizes
sm 32px · md 38px (default, spec) · lg 44px.
States
Error switches the border and focus ring to the danger tokens and sets aria-invalid. Disabled flattens to a neutral treatment and hides the clear affordance.
Empty results
When nothing matches the query the list shows an empty message — customise it with emptyMessage.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| items | ComboboxItem[] | — | Options to pick from. See the item shape below. |
| label | string | — | Field label rendered above the control. |
| placeholder | string | "Select…" | Text shown in the closed field when nothing is selected. |
| searchPlaceholder | string | "Search…" | Placeholder of the search input inside the popup. |
| emptyMessage | string | "No matches" | Message shown when no option matches the query. |
| value | string | null | — | Selected item value. Use with onValueChange for a controlled combobox. |
| defaultValue | string | null | — | Initial selection when uncontrolled. |
| onValueChange | (value: string | null) => void | — | Called with the selected item's value, or null when cleared. |
| clearable | boolean | true | Shows a clear affordance in the field while a value is selected. |
| size | "sm" | "md" | "lg" | "md" | Control height: 32 / 38 / 44px. |
| error | boolean | false | Danger border + focus ring, sets aria-invalid. |
| disabled | boolean | false | Blocks interaction and flattens the field. |
| name | string | — | Identifies the field when a form is submitted. |
| className | string | — | Class applied to the outer wrapper. |
| Prop | Type | Default | Description |
|---|---|---|---|
| ComboboxItem.value | string | — | Unique value identifying the option. |
| ComboboxItem.label | string | — | Row label; also what the search filter matches. |
| ComboboxItem.sub | string | — | Secondary line under the label — role, team, count. |
| ComboboxItem.lead | ReactNode | — | Leading visual — avatar, icon, or colour dot. |
| ComboboxItem.disabled | boolean | false | Renders the option non-interactive. |
Usage
Reach for a combobox when a select would exceed ~8 options. For a handful of fixed choices use a Select; for multiple values use a token input.
Use it past ~8 options
When a list is long enough to scroll, search beats scanning. Below that, a plain Select is simpler.
Use it for multi-select
One value per combobox. For tags or multiple assignees, use a token / multi-select input instead.
Show a secondary line
Role, team or count under the label disambiguates similar names and speeds recognition.
Hide the current value
Always render the selection in the closed field, with its avatar or icon, so state is obvious at rest.
Keyboard & accessibility
Full ARIA combobox semantics from Base UI: the trigger is a button labelled by the field label, the popup search input controls the listbox, and the highlighted option is announced. Hands never leave the keyboard.