Combobox

Combobox

An autocomplete input that filters a popup list as you type, built on Base UI. Single or multiple selection, with a chips layout for multi-select.

Playground

placeholder
variant
size
multiple
<Combobox items={fruits}>
  <ComboboxInput placeholder="Pick a fruit..." />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxCollection>
        {(item) => (
          <ComboboxItem key={item.value} value={item}>
            {item.label}
          </ComboboxItem>
        )}
      </ComboboxCollection>
    </ComboboxList>
    <ComboboxEmpty>No fruits found.</ComboboxEmpty>
  </ComboboxContent>
</Combobox>

Preview

Pass items to the root, an ComboboxInput to type into, and a ComboboxContent popup with a ComboboxCollection render function for the filtered items.

const fruits = [
  { label: "Apple", value: "apple" },
  { label: "Banana", value: "banana" },
  { label: "Cherry", value: "cherry" },
]

// Passing the whole { value, label } object as the item value makes the
// input display the label and forms submit the value – automatically.
<Combobox items={fruits}>
  <ComboboxInput placeholder="Pick a fruit..." />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxCollection>
        {(item) => (
          <ComboboxItem key={item.value} value={item}>
            {item.label}
          </ComboboxItem>
        )}
      </ComboboxCollection>
    </ComboboxList>
    <ComboboxEmpty>No results found.</ComboboxEmpty>
  </ComboboxContent>
</Combobox>

Installation

npx shadcn@latest add https://espresso-base-ui.vercel.app/r/combobox.json

Usage

import {
  Combobox,
  ComboboxChip,
  ComboboxChips,
  ComboboxChipsInput,
  ComboboxCollection,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxList,
  ComboboxSeparator,
  ComboboxTrigger,
  ComboboxValue,
  useComboboxAnchor,
} from "@/components/ui/combobox"
<Combobox items={items}>
  <ComboboxInput placeholder="Search..." />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxCollection>
        {(item) => (
          <ComboboxItem key={item.value} value={item}>
            {item.label}
          </ComboboxItem>
        )}
      </ComboboxCollection>
    </ComboboxList>
    <ComboboxEmpty>No results found.</ComboboxEmpty>
  </ComboboxContent>
</Combobox>

Variants

variant on the root styles the input surface: outline (default, raised background with shadow), subtle (filled secondary surface) and ghost (transparent until focused).

<Combobox variant="outline" items={fruits}>...</Combobox>
<Combobox variant="subtle" items={fruits}>...</Combobox>
<Combobox variant="ghost" items={fruits}>...</Combobox>

Sizes

size on the root scales the input, the popup radius and the item density: xs, sm, md (default) and lg.

<Combobox size="xs" items={fruits}>...</Combobox>
<Combobox size="sm" items={fruits}>...</Combobox>
<Combobox size="md" items={fruits}>...</Combobox>
<Combobox size="lg" items={fruits}>...</Combobox>

Grouped items

Wrap items in ComboboxGroup with a ComboboxLabel heading, and divide groups with ComboboxSeparator.

<Combobox>
  <ComboboxInput placeholder="Search produce..." />
  <ComboboxContent>
    <ComboboxList>
      <ComboboxGroup>
        <ComboboxLabel>Fruits</ComboboxLabel>
        {fruits.map((item) => (
          <ComboboxItem key={item.value} value={item}>
            {item.label}
          </ComboboxItem>
        ))}
      </ComboboxGroup>
      <ComboboxSeparator />
      <ComboboxGroup>
        <ComboboxLabel>Vegetables</ComboboxLabel>
        {vegetables.map((item) => (
          <ComboboxItem key={item.value} value={item}>
            {item.label}
          </ComboboxItem>
        ))}
      </ComboboxGroup>
    </ComboboxList>
    <ComboboxEmpty>No items found.</ComboboxEmpty>
  </ComboboxContent>
</Combobox>

Multi-select chips

With multiple, render the selection as removable chips: ComboboxChips wraps ComboboxChip entries and a ComboboxChipsInput. Use the exported useComboboxAnchor ref so the popup anchors to the whole chips container instead of the inline input.

const [value, setValue] = React.useState([fruits[0], fruits[2]])
const anchorRef = useComboboxAnchor()

<Combobox multiple items={fruits} value={value} onValueChange={setValue}>
  <ComboboxChips ref={anchorRef}>
    {value.map((item) => (
      <ComboboxChip key={item.value}>
        <ComboboxValue>{item.label}</ComboboxValue>
      </ComboboxChip>
    ))}
    <ComboboxChipsInput placeholder="Add fruits..." />
  </ComboboxChips>
  <ComboboxContent anchor={anchorRef}>
    <ComboboxList>...</ComboboxList>
    <ComboboxEmpty>No fruits found.</ComboboxEmpty>
  </ComboboxContent>
</Combobox>

API reference

Combobox wraps the Base UI Combobox root and shares its variant and size with every part via context. The root also accepts all Base UI root props – items, value, defaultValue, onValueChange, multiple and disabled. When item values are objects shaped { value, label }, the input displays the label and forms submit the value automatically; other object shapes can map themselves with itemToStringLabel (display) and itemToStringValue (form submission).

Combobox

PropTypeDefaultDescription
variant"outline" | "subtle" | "ghost""outline"Input surface style, shared with all parts via context.
size"xs" | "sm" | "md" | "lg""md"Scales the input, popup radius, item density and chips.
itemsTItem[]–Options to filter as the user types. Objects with label/value work with the default filtering.
multiplebooleanfalseAllow selecting several values; pair with the chips layout.
value / onValueChangeTItemValue | TItemValue[]–Controlled selection and its change handler.

ComboboxInput

PropTypeDefaultDescription
showTriggerbooleantrueShow the chevron button that opens the popup at the trailing edge.
showClearbooleanfalseShow a clear button that resets the selection. When visible it replaces the trigger chevron.

ComboboxContent

PropTypeDefaultDescription
side"top" | "right" | "bottom" | "left""bottom"Which side of the anchor the popup opens on.
sideOffsetnumber6Gap between the anchor and the popup.
align"start" | "center" | "end""start"Alignment against the anchor.
alignOffsetnumber0Offset along the alignment axis.
anchorReact.RefObject<HTMLElement>–Custom anchor element – pass the useComboboxAnchor ref to anchor the popup to a ComboboxChips container.

ComboboxChip

PropTypeDefaultDescription
showRemovebooleantrueRender the small remove (x) button inside the chip.
ComponentDescription
ComboboxInputSingle-select text input rendered inside an InputGroup, with a built-in trigger chevron and optional clear button.
ComboboxContentPortalled popup (data-slot="combobox-content") sized to the anchor width, with open/close animations.
ComboboxListScrollable option list (data-slot="combobox-list") with scroll fade and max height.
ComboboxItemSelectable option (data-slot="combobox-item") with highlight styles and a trailing check indicator when selected.
ComboboxCollectionRender-function wrapper (data-slot="combobox-collection") that maps the filtered items to elements.
ComboboxGroup / ComboboxLabelOption group (data-slot="combobox-group") with a small heading (data-slot="combobox-label").
ComboboxSeparatorThin divider between groups (data-slot="combobox-separator").
ComboboxEmptyMessage shown when no items match the query (data-slot="combobox-empty").
ComboboxChips / ComboboxChip / ComboboxChipsInputMulti-select layout: chips container (data-slot="combobox-chips"), a removable chip per value (data-slot="combobox-chip", remove button data-slot="combobox-chip-remove") and the inline input (data-slot="combobox-chip-input").
ComboboxTriggerStandalone popup trigger with a chevron icon (data-slot="combobox-trigger"), used internally by ComboboxInput.
ComboboxValueRenders the selected value label (data-slot="combobox-value"), e.g. inside a chip.
useComboboxAnchorConvenience hook returning a ref to share between ComboboxChips and ComboboxContent anchor.

Accessibility & styling hooks

Base UI wires up the full combobox ARIA pattern – the input gets role="combobox" with aria-expanded, the popup is a listbox whose options report aria-selected, and arrow keys, Enter and Escape work out of the box. Every part exposes a data-slot attribute (see the parts table above), variant-aware parts also reflect data-variant and data-size, and the popup carries Base UI state attributes like data-open and data-side – target these from CSS for app-level overrides.

Search documentation

Search the component docs