Combobox

Filterable selection control for searchable option lists.

Quick Preview

Composition

Use this public composition when building Combobox:

Combobox
├── ComboboxTrigger        # or ComboboxInput, or ComboboxChips
│   └── ComboboxValue
└── ComboboxContent
    ├── ComboboxInput
    ├── ComboboxEmpty
    └── ComboboxList
        └── ComboboxItem

ComboboxInput appears twice in that tree because it is two things. Inside ComboboxContent it is the popup's search box: squared off to sit above the list, and left out of the popup's anchoring, since the trigger is what the popup lines up with. Anywhere else it is the field itself — it anchors the popup to its full width, chevron and padding included, rather than to the bare <input> inside it.

Usage

Pass items on the root — built-in text filtering only runs against items; inline-only children render but do not filter as the user types.

<Combobox items={areas} defaultValue="Lekki">
  <ComboboxTrigger><ComboboxValue placeholder="Choose area" /></ComboboxTrigger>
  <ComboboxContent>
    <ComboboxInput placeholder="Search areas" showTrigger={false} />
    <ComboboxEmpty>No areas found</ComboboxEmpty>
    <ComboboxList>
      {(area) => <ComboboxItem key={area} value={area}>{area}</ComboboxItem>}
    </ComboboxList>
  </ComboboxContent>
</Combobox>

Examples

Optional actions

Use the header and footer slots for actions that must remain outside the option list. The footer stays visible while a long result list scrolls.

Sizes

Use size to align trigger, input, chips, and list item density.

Input as the Trigger

Drop ComboboxTrigger and put ComboboxInput in its place when the field itself should be typed into, rather than opening a popup that holds the search box. Filtering, selection, and the chevron work the same; what changes is that the field is on the page rather than in the popup.

The popup anchors to the whole field — padding, chevron, and clear button included — not to the <input> inside it. Use showClear to add the clear button and showTrigger={false} to drop the chevron.

The popup matches the field's full width, chevron and padding included.

Multiple Selection

Set multiple when users can choose more than one option from a searchable list.

Chips

Compose ComboboxChips with ComboboxChip and ComboboxChipsInput to keep the selection in the field. Chips take their value from their position against the selection, so render them in the order value holds.

A chips field grows as selections wrap onto a second row, and a pill radius that looks right on one row reads as a mistake on two — so any trigger taller than its own min-height trades the pill for a box. That applies to ComboboxTrigger and ComboboxInput as well as to ComboboxChips: each size variant sets a min-h-* beside its fixed h-*, so a call site that swaps in h-auto keeps both the control's floor and the measurement.

The chips container is not the popup's anchor by default. Pass its ref through useComboboxAnchor and hand it to ComboboxContent so the popup matches the field's width.

Remove areas until the field fits on one row to see the pill return.

Virtualized

For very large option sets, mark the root virtualized and render items through ComboboxVirtualizedList so only the visible rows are mounted. Use useComboboxVirtualizer to bridge keyboard highlight scrolling, and pass itemToStringLabel so filtering and the selected value can resolve a label from each item object.

The virtualized viewport has a fixed maxHeight (default "22rem") rather than tracking the popup's available height. This is intentional: a resize-driven height would feed back into the positioner and freeze the popup on open for large lists. Rows are also rendered at a fixed estimateSize, so match it to your item size variant when you customise it.

Props

Prop/APITypeDefaultDescription
itemsValue[]-Options to filter as the user types. Required for built-in filtering.
value / defaultValue / onValueChangebase-ui Combobox props-Controlled or uncontrolled selection.
size`"sm""md""lg"`
multiplebooleanfalseEnables multi-selection chips.
showTrigger / showClearbooleantrue / falseComboboxInput inline chevron and clear affordances.
virtualized / itemToStringLabelbase-ui Combobox props-Enable external virtualization; provide a label getter for object items.
ComboboxVirtualizedList{ virtualizerRef, estimateSize?, overscan?, maxHeight?, children }estimateSize: 40, maxHeight: "22rem"Fixed-row virtualization via @tanstack/react-virtual; place inside ComboboxList. maxHeight must stay constant — never wire it to --available-height.
useComboboxVirtualizer()→ { virtualizerRef, onItemHighlighted }-Bridges highlight scrolling; spread onItemHighlighted on the root.
useComboboxAnchor()→ ref-Anchors the popup to a chips field. Put it on ComboboxChips and pass it to ComboboxContent's anchor.
data-grown"true"-Set on a trigger, input, or chips field that is taller than its own min-height. Drives the box radius; style against it for anything else that should change with the row count.
classNamestring-Local layout or spacing overrides.
native/root propsReact component props-Passed through to the underlying root or primitive.

Accessibility

  • Preserve the documented Combobox composition so labels, triggers, content, and controls remain connected.
  • Provide visible labels or accessible names for interactive controls.
  • Preserve the component-provided focus, keyboard, disabled, and invalid-state behavior.
  • Do not communicate state with color alone; include text, icons, or helper copy.

On this page