Combobox

A searchable selection component that combines a text input with a dropdown list, allowing users to filter and select from available options

Installation

pnpm add @wandercom/design-system-web

Usage

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from '@wandercom/design-system-web/ui/combobox';

const options = ['Option 1', 'Option 2', 'Option 3'];

export function Example() {
  return (
    <Combobox items={options}>
      <ComboboxInput placeholder="Search..." />
      <ComboboxContent>
        <ComboboxEmpty>No results found.</ComboboxEmpty>
        <ComboboxList>
          {(item) => (
            <ComboboxItem key={item} value={item}>
              {item}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  );
}

Examples

The default example shows a basic combobox with type-to-filter, a variant with the clear button enabled, and the disabled state.

Loading example...
const destinations = ['Aspen', 'Big Bear', 'Joshua Tree', 'Lake Tahoe', 'Malibu'];

<Combobox items={destinations}>
  <ComboboxInput placeholder="Search destinations..." />
  <ComboboxContent>
    <ComboboxEmpty>No destinations found.</ComboboxEmpty>
    <ComboboxList>
      {(item) => (
        <ComboboxItem key={item} value={item}>
          {item}
        </ComboboxItem>
      )}
    </ComboboxList>
  </ComboboxContent>
</Combobox>

<Combobox items={destinations}>
  <ComboboxInput placeholder="With clear button" showClear />
  <ComboboxContent>
    <ComboboxEmpty>No destinations found.</ComboboxEmpty>
    <ComboboxList>
      {(item) => (
        <ComboboxItem key={item} value={item}>
          {item}
        </ComboboxItem>
      )}
    </ComboboxList>
  </ComboboxContent>
</Combobox>

<Combobox disabled items={destinations}>
  <ComboboxInput placeholder="Disabled combobox" />
  <ComboboxContent>
    <ComboboxList>
      {(item) => (
        <ComboboxItem key={item} value={item}>
          {item}
        </ComboboxItem>
      )}
    </ComboboxList>
  </ComboboxContent>
</Combobox>

With object values

When item values are objects with a { value, label } shape, Base UI automatically uses label for display and filtering, while value is used for form submission. This is useful when the display text differs from the underlying ID.

Loading example...
const destinations = [
  { value: 'aspen-co', label: 'Aspen, Colorado' },
  { value: 'big-bear-ca', label: 'Big Bear, California' },
  { value: 'joshua-tree-ca', label: 'Joshua Tree, California' },
];

<Combobox items={destinations}>
  <ComboboxInput placeholder="Search destinations..." />
  <ComboboxContent>
    <ComboboxEmpty>No destinations found.</ComboboxEmpty>
    <ComboboxList>
      {(item) => (
        <ComboboxItem key={item.value} value={item}>
          {item.label}
        </ComboboxItem>
      )}
    </ComboboxList>
  </ComboboxContent>
</Combobox>

Controlled

When managing the selected value externally, pass both value/onValueChange and inputValue/onInputValueChange. Base UI updates the input text via onInputValueChange when an item is selected, so both callbacks are required for the input to reflect the selection.

Loading example...
const [value, setValue] = useState<Destination | null>(null);
const [inputValue, setInputValue] = useState('');

<Combobox
  inputValue={inputValue}
  items={destinations}
  value={value}
  onInputValueChange={setInputValue}
  onValueChange={setValue}
>
  <ComboboxInput placeholder="Search destinations..." />
  <ComboboxContent>
    <ComboboxEmpty>No destinations found.</ComboboxEmpty>
    <ComboboxList>
      {(item) => (
        <ComboboxItem key={item.value} value={item}>
          {item.label}
        </ComboboxItem>
      )}
    </ComboboxList>
  </ComboboxContent>
</Combobox>

With groups

Use ComboboxGroup, ComboboxLabel, and ComboboxSeparator to organize options into labeled sections.

Loading example...
const regionGroups = [
  { value: 'Mountains', items: ['Aspen', 'Big Bear', 'Telluride'] },
  { value: 'Desert', items: ['Joshua Tree', 'Palm Springs', 'Sedona'] },
  { value: 'Coast', items: ['Malibu', 'Carmel', 'Cannon Beach'] },
];

<Combobox items={regionGroups}>
  <ComboboxInput placeholder="Search destinations..." />
  <ComboboxContent>
    <ComboboxEmpty>No destinations found.</ComboboxEmpty>
    <ComboboxList>
      {(group) => (
        <ComboboxGroup items={group.items} key={group.value}>
          <ComboboxLabel>{group.value}</ComboboxLabel>
          <ComboboxCollection>
            {(item) => (
              <ComboboxItem key={item} value={item}>
                {item}
              </ComboboxItem>
            )}
          </ComboboxCollection>
        </ComboboxGroup>
      )}
    </ComboboxList>
  </ComboboxContent>
</Combobox>

Props

Combobox

items:

Value[] | { value: string; items: Value[] }[]
The items to display and filter. Pass a flat array for a simple list, or an array of groups with nested items for grouped layouts. Base UI filters these items against the input value automatically.

value?:

Value | Value[]
The controlled selected value.

defaultValue?:

Value | Value[]
The default selected value when the component is first rendered.

onValueChange?:

(value: Value, eventDetails: ChangeEventDetails) => void
Callback fired when the selected value changes.

inputValue?:

string
The controlled input value. When provided, you must also pass onInputValueChange to keep the input in sync with selections.

onInputValueChange?:

(value: string, eventDetails: ChangeEventDetails) => void
Callback fired when the input value changes, including when an item is selected. Required when inputValue is controlled.

disabled?:

boolean
Disables the combobox when true.

multiple?:

boolean
Enables multi-select mode.

open?:

boolean
The controlled open state of the combobox popup.

onOpenChange?:

(open: boolean) => void
Callback fired when the open state changes.

ComboboxInput

placeholder?:

string
Placeholder text displayed when the input is empty.

showTrigger?:

boolean
Whether to show the dropdown trigger button. Defaults to true.

showClear?:

boolean
Whether to show the clear button. Defaults to false.

disabled?:

boolean
Disables the input.

className?:

string
Additional CSS classes to apply.

ComboboxContent

side?:

'top' | 'bottom' | 'left' | 'right'
Which side of the anchor to position against. Defaults to 'bottom'.

sideOffset?:

number
Offset from the anchor edge in pixels. Defaults to 6.

align?:

'start' | 'center' | 'end'
Alignment along the anchor edge. Defaults to 'start'.

alignOffset?:

number
Offset for alignment in pixels. Defaults to 0.

anchor?:

React.RefObject<HTMLElement>
Custom anchor element for positioning the popup.

className?:

string
Additional CSS classes to apply.

ComboboxItem

value:

string
The unique value of this combobox item.

disabled?:

boolean
Disables this specific item.

className?:

string
Additional CSS classes to apply.

ComboboxLabel

className?:

string
Additional CSS classes to apply to the label.

ComboboxEmpty

className?:

string
Additional CSS classes to apply.

ComboboxSeparator

className?:

string
Additional CSS classes to apply to the separator.

ComboboxChips

className?:

string
Additional CSS classes to apply to the chips container.

ComboboxChip

showRemove?:

boolean
Whether to show the remove button. Defaults to true.

className?:

string
Additional CSS classes to apply.

Accessibility

The Combobox component is built on the Base UI Combobox primitive, which implements the WAI-ARIA combobox pattern for accessible searchable selection.

  • Supports keyboard interaction: type to filter options, arrow keys to navigate the list, Enter to select the highlighted item, and Escape to close the popup
  • Screen reader announces the count of filtered results as the user types
  • Focus management ensures visible focus indicators during keyboard navigation
  • Disabled items apply data-disabled and are excluded from keyboard navigation
  • Multi-select mode uses chips with accessible remove buttons for deselecting values
Combobox