useCombobox

useCombobox owns a combobox's open state, value, input, highlight and ids. Components own the chrome: Field for the input, Popover, BottomSheet or Dialog for the surface, OptionList for the list. There is no Combobox.* export. The Combobox recipe shows single, multi, sheet, async and create.

presentation defaults to 'auto', which picks a sheet on a coarse pointer or a narrow viewport through useSheetPresentation. Force popover or sheet to keep it fixed, as the demo below forces popover. dialog puts the input and list in a Dialog, as the Command palette does. Composition lists which getter goes on which part.

React
Redux
Svelte

When to use

When: Searchable select — typeahead + list. Hook owns open/value/input; Field + Popover/BottomSheet + OptionList own chrome, as the recipe shows. Auto sheet on coarse/narrow.

When not: Static on-page exclusive → Radio. Non-search list → usePopover + OptionList only. Action menu → Menu. Exporting Combobox.* chrome (forbidden).

Props

useCombobox props
NameTypeRequiredDefaultDescription
selectionMode"single" | "multiple"no"single"Single uses TextInput. Multiple uses ChipInput.
optionsreadonly ComboboxOption[]no—Sync options. Ignored when loadOptions is set.
loadOptions(query: string, signal: AbortSignal) => Promise<readonly ComboboxOption[]>no—Async options. Stale responses are discarded. An inline function is fine; it is read at call time.
loadKeystring | numberno—Reloads options when it changes. Use it when loadOptions depends on state outside the query, such as a category.
presentation"popover" | "sheet" | "dialog" | "auto"no"auto"auto uses useSheetPresentation. dialog is for Dialog.Root, through getDialogProps.
allowCreatebooleannofalseShow a create row when the query has no case-insensitive label match. With loadOptions, the row waits until the load for that query settles.
valuestring | null | readonly string[]no—Controlled value. Null for empty single. Array for multiple.
openbooleanno—Controlled open.
inputValuestringno—Controlled filter text.