Combobox
A text input that filters a list of options as people type, for picking from long lists.
Preview
Installation
pnpm add @syntara/react @syntara/tokensimport { Combobox, ComboboxItem, ComboboxSection } from '@syntara/react';Usage
import { Combobox, ComboboxItem } from '@syntara/react';
export function CountryField() {
return (
<Combobox label="Country" placeholder="Search countries…" onSelectionChange={(key) => console.log(key)}>
<ComboboxItem id="in">India</ComboboxItem>
<ComboboxItem id="id">Indonesia</ComboboxItem>
<ComboboxItem id="jp">Japan</ComboboxItem>
</Combobox>
);
}Examples
Custom value
allowsCustomValue keeps text that isn't in the list.
Sections, icons and descriptions
Group options under headings; items can have an icon and a second line.
Invalid and disabled
Required with an error message, and a disabled combobox.
Accessibility
| Keys | Action |
|---|---|
| Type characters | Filters the list and opens it. |
| ↓or↑ | Opens the list; moves the highlighted option while focus stays in the input. |
| Enter | Chooses the highlighted option and closes the list. |
| Escape | Closes the list and puts back the text of the selected option (typed text is kept with allowsCustomValue). |
| Tab | Commits the current text or selection and moves focus on. |
- The input is role="combobox" with aria-expanded, aria-controls and aria-activedescendant; the highlighted option is announced as you arrow.
- The chevron button is for pointer users and is excluded from the tab order.
- The empty state is rendered inside the listbox so screen readers announce it.
- Motion follows prefers-reduced-motion: the list still fades, but the spring entrance, the rows stepping in on open (first 8, 30ms apart; not while filtering), the press dip, the field's growing halo and the chevron flip only run when motion is allowed.
Guidelines
Do
- Use for long lists (15+ options) people know by name: countries, people, accounts.
- Write a placeholder that says what can be searched.
- Use allowsCustomValue only when free text is a valid answer.
Don’t
- Don't use for short lists — a Select is quicker to scan.
- Don't use for search that navigates or runs actions — use SearchField or Command.
- Don't hide the label behind the placeholder.
API reference
Combobox
labelReactNodeVisible label. Use aria-label only when a visible label is impossible.
descriptionReactNodeHelp text under the input, linked with aria-describedby.
errorMessagestring | ((validation: ValidationResult) => string)Shown under the input when the field is invalid.
placeholderstringPlaceholder text in the input.
emptyStateReactNodeRow shown when nothing matches the typed text.
Default
'No results'items / defaultItemsIterable<T>Items for a dynamic list. Use defaultItems to let the combobox filter them; items when you filter yourself.
childrenRequiredReactNode | ((item: T) => ReactElement)ComboboxItem and ComboboxSection elements, or a function that renders one per item.
selectedKey / defaultSelectedKeyKey | nullThe selected item's id, controlled or initial.
onSelectionChange(key: Key | null) => voidCalled when an option is chosen or the selection is cleared.
inputValue / defaultInputValuestringThe text in the input, controlled or initial.
onInputChange(value: string) => voidCalled as the text changes.
allowsCustomValuebooleanKeep typed text that doesn't match an option instead of reverting on blur.
Default
falsemenuTriggerinputdefaultfocusmanual
What opens the list: typing, focusing, or only the button and arrow keys.
defaultFilter(textValue: string, inputValue: string) => booleanCustom filter for defaultItems. Defaults to a locale-aware 'contains'.
allowsEmptyCollectionbooleanKeep the list open to show emptyState when nothing matches.
Default
truesizesmmddefaultlg
Height of the box: md is the density's control height, sm one 8px step shorter, lg two steps taller. Height, corner radius and inline inset match a Button of the same size, so a field and a button in one row line up.
isRequired / isInvalid / isDisabledbooleanField states; isRequired also shows the label's required marker.
Default
false
ComboboxItem
idRequiredKeyUnique key used by selectedKey and onSelectionChange.
childrenRequiredReactNodeThe option label. A string also becomes its textValue, which fills the input when chosen.
textValuestringPlain text used for filtering and the input when children is not a string.
descriptionReactNodeA second, quieter line under the label.
iconReactNodeDecorative leading icon.
ComboboxSection
titleReactNodeHeading for the group; also its accessible name. Sections with no matches hide while filtering.
Tokens
The semantic tokens this component reads, grouped by what they control. Swatches show this site’s theme; change a tenant’s brand and the component follows with no code change.
- Colour12
text.subtletext.defaultfocus.ringtext.disabledborder.defaultsurface.raisedborder.subtlesurface.selectedborder.strongsurface.defaultsurface.sunkensurface.canvas- Type8
font.size.mdfont.bodyline-height.normalfont.size.xsfont.weight.mediumline-height.snugfont.size.smfont.tracking.sm- Space and size7
space.2space.1control-heightspace.6space.16control-padding-inlinespace.4- Shape2
radius.containerradius.field- Depth4
glass.opacityglass.blurshadow.overlayhairline- Motion6
motion.duration.fastmotion.easingmotion.duration.springmotion.springmotion.duration.normalmotion.easing-out- Other2
sheenrim