Combobox
A field the user types into, with a panel of the items the app supplies for the current text. It takes one value, several as chips, or whatever was typed. It owns its label, its error message, and its busyness; the app owns the items, the value, and the timing.
The app owns the list
Combobox never filters, fetches, or debounces. You pass the items you want shown for the current text, and nothing else is shown. That keeps a billed autocomplete call, its debounce, and its cancellation in your code, where you can see them. Read the text through onInputValueChange, fetch how you like, and render the answer as ComboboxItem children.
Six parts are exported: Combobox, ComboboxItem, ComboboxGroup, ComboboxLabel, ComboboxSeparator, and ComboboxEmpty. There is no input, trigger, or content part — the root renders all of them, and className styles the wrapper that holds the label and the error message.
A strict place field
mode is required and has no default. In ComboboxMode.Single without allowFreeText the field is strict: the value is always an item key, and text that matches no pick reverts on blur. The demo debounces its own fetch, shows loading while the request runs, and renders a ComboboxEmpty when the source comes back with nothing. Type r or ha to see matches, or zz to see the empty row.
Powered by Foursquare
Powered by Foursquare
Groups, separators, and descriptions
ComboboxGroup is semantics only and takes a ComboboxLabel as its heading; ComboboxSeparator rules a line between groups. An item'sdescription sits after its label on the same line and truncates, and its icon sits ahead of the label, hidden from screen readers so only the label is read. The icon stays in the list: the input and the chips carry the label alone. A disabled item stays in the list so positions never shift. Open the panel below to see all of it.
Free text as the value
allowFreeText lets the field keep what was typed. A pick reports the item's key; text that matches no pick is reported as itself on blur. This is the shape a Destination field wants — a traveller may be going somewhere your source has never heard of.
A typed string that happens to equal a real item key is indistinguishable from a pick. Keys like the place ids above make that collision negligible; keys that read like words do not, so key your items with ids rather than labels.
Several values as chips
ComboboxMode.Multiple takes a string[] value. A pick adds a chip before the caret, keeps the panel open, marks the item with a check, and clears the text so the next query starts fresh; picking a checked item removes it. Backspace on an empty input removes the last chip, ArrowLeft from the start of the caret focuses it, and Backspace or Delete there removes it.
Chips from free text
With allowFreeText in multiple mode, Enter with nothing highlighted turns the typed text into a chip. Comma and blur do not commit: commas occur inside place names, and a blur commit turns an abandoned keystroke into a value.
Error and disabled
error takes the destructive border and ring, turns the label destructive, and renders the message below the field through the same FieldErrorMessage as Input; the panel may cover it while open. disabled dims the whole field and takes it out of the tab order.
Pick a city we fly from
Pick a city we fly from
Sizes
Two field sizes match Input and Select. Items stay 32px at both sizes, and in multiple mode the field grows by rows as chips wrap.
Attribution is app content
A place source usually requires a credit line. Combobox has no footer slot and never will: the "Powered by" line under the strict place field above is rendered by the demo, not by the component. Your source, your wording, your placement.
Motion
The panel enters and exits on the floating item's anchored keyframe pair — scale from 0.96 plus fade, 250ms on the bounce curve in and 150ms on the settle curve out, growing from the field. The chevron rotates 180 degrees at --motion-base, and the field's border transitions at --motion-fast. Chips enter and exit on springSettle and their neighbours reflow with motion's layout prop, because a chip displaces the caret. The item highlight, the check, the clear button, and the spinner all snap.
Accessibility
The input is the only tab stop. The panel opens on typing, ArrowDown, ArrowUp, or the chevron, and never on focus alone. Escape closes and never clears, because with free text the typed text is the value. Tab and Shift-Tab close without selecting and move on — the panel is non-modal, unlike Select — and focus never leaves the input when the panel opens or closes; the highlighted row is the focus indicator, and the input carries aria-activedescendant. Home and End stay the caret's, so a long query is still editable from either end. The clear and chevron buttons are labelled and out of the tab order, so neither adds a stop between fields.
The panel registers in the same layer stack as Dialog and DropdownMenu, so it opens, positions, and stays clickable inside a modal dialog. name posts a hidden input per value — the key for a pick, the string for free text — and required sets aria-required on the input.