|Kit

Combobox

The kit's composable search-select primitive — a thin styled layer over Ark UI's Combobox. The engine Autocomplete and MultiSelect build on, and the escape hatch for fully custom pickers.

Combobox is the low-level, composable building block for searchable selects. It's a thin styled layer over Ark UI's Combobox, exposing the parts so you assemble the control and dropdown yourself. The data-driven Autocomplete and MultiSelect are built on it — reach for Combobox directly only when those don't fit.

Want "just pass data"? Use Autocomplete / MultiSelect / Select. Combobox is the composition layer underneath.

Playground

Combobox

Loading preview

Props

Code

<Combobox size="md" placeholder="Search frameworks…" />

Anatomy

You own the collection and the filtering — Ark's contract. Build a collection with createListCollection (re-exported from the module), filter your source list on input change, and render a Combobox.Item per option. size / variant / color set once on Root flow to the parts.

'use client';
import { useMemo, useState } from 'react';
import { Combobox, createListCollection } from '@42-central/ui-react/combobox';

const PROJECTS = ['Libft', 'ft_printf', 'Get Next Line', 'Minishell', 'ft_transcendence'];

export function ProjectPicker() {
  const [items, setItems] = useState(PROJECTS);
  const collection = useMemo(
    () => createListCollection({ items, itemToValue: (i) => i, itemToString: (i) => i }),
    [items],
  );

  return (
    <Combobox.Root
      collection={collection}
      openOnClick
      onInputValueChange={({ inputValue }) => {
        const q = inputValue.toLowerCase().trim();
        setItems(q ? PROJECTS.filter((f) => f.toLowerCase().includes(q)) : PROJECTS);
      }}
    >
      <Combobox.Control>
        <Combobox.Input placeholder="Search projects…" />
        <Combobox.ClearTrigger />
        <Combobox.Trigger />
      </Combobox.Control>

      <Combobox.Content>
        {items.map((item) => (
          <Combobox.Item key={item} item={item}>{item}</Combobox.Item>
        ))}
        <Combobox.Empty>Nothing found</Combobox.Empty>
      </Combobox.Content>
    </Combobox.Root>
  );
}

Parts

PartWraps (Ark)Notes
Combobox.RootCombobox.RootTakes the collection. Adds size / variant / color; defaults the dropdown to the control's width (positioning.sameWidth). Ark's translations (triggerLabel, clearTriggerLabel) default to the kit's English — "Show options" and "Clear value", the same wording as the rest of the combobox family — and a partial override keeps the other key. All Ark root props (value, inputValue, multiple, onValueChange, onInputValueChange, …) pass through. Accepts testId, landing on the root element.
Combobox.ControlCombobox.ControlThe input shell (same chrome as Input). Holds the input + triggers. Accepts startSlot / endSlot for inline-start / -end adornments, laid out like Input's slots. Accepts testId, landing on this shell element.
Combobox.InputCombobox.InputThe search <input>. Accepts testId, landing on this <input>.
Combobox.TriggerCombobox.TriggerChevron toggle; rotates when open.
Combobox.ClearTriggerCombobox.ClearTriggerClears the value; auto-hidden when empty.
Combobox.ContentPositioner + ContentThe floating panel (positioner + scroll container in one).
Combobox.ItemItem + ItemText + ItemIndicatorAn option. Pass item={collectionItem}; renders a check when selected. Accepts testId, landing on this option's element.
Combobox.GroupItemGroup + ItemGroupLabelPass label to head a group of options.
Combobox.EmptyEmptyShown when the collection is empty (e.g. no matches).
Combobox.ContextCombobox.ContextRender-prop access to the live machine api.

Root, Control, Input, and Item each take their own testId prop for a stable data-testid on their respective rendered element — see the Testing guide. Trigger, ClearTrigger, Group, and Empty deliberately don't get an explicit testId: they're thin Ark re-exports, and Ark's own data-part attribute (e.g. [data-part="trigger"], [data-part="clear-trigger"]) already gives a free, stable selector on those parts without any extra prop.

Grouped options

Wrap options in Combobox.Group with a label:

<Combobox.Content>
  <Combobox.Group label="Common Core">
    <Combobox.Item item="libft">Libft</Combobox.Item>
    <Combobox.Item item="ft_printf">ft_printf</Combobox.Item>
  </Combobox.Group>
  <Combobox.Group label="Outer Core">
    <Combobox.Item item="ft_transcendence">ft_transcendence</Combobox.Item>
    <Combobox.Item item="inception">Inception</Combobox.Item>
  </Combobox.Group>
</Combobox.Content>

Multiple selection

Pass multiple to Combobox.Root and pair it with a PillsInput target to show selected values as pills — this is exactly what MultiSelect assembles for you.

Field integration

Combobox consumes Ark's Field context, so wrapping it in Field wires the label, description, error, and aria-* automatically:

<Field label="Project" description="Pick a project">
  <ProjectPicker />
</Field>

Accessibility

  • Full combobox keyboard model from Ark: type to filter, ↑ / ↓ to move, Enter to select, Esc to close, Home / End to jump.
  • role="combobox" / listbox / option and the aria-activedescendant / aria-expanded wiring are handled by the Ark machine.
  • RTL is honored via the document dir. Filtering, custom values, and selection behavior are all Ark-owned — see the Ark Combobox docs for the full prop surface forwarded through Combobox.Root.

Changelog

v0.19.0
  • A translations prop for the built-in trigger, clear, remove, search, increment/decrement and upload labels

    #331
  • Combobox.Root's trigger is named "Show options" by default, the kit's English, instead of zag's "Toggle suggestions"

    #331
v0.18.0
  • asChild is a compile error on Combobox.Control, Group, Content, Panel and Item, Drawer/Modal.Positioner, Drawer.Grabber, Popover.Content and Tooltip.Arrow

    #294
  • Combobox.Trigger and ClearTrigger keep their icon under asChild

    #288
v0.16.0
  • An outside click no longer dismisses the parent while a nested overlay is open

    #251
v0.9.0
  • Opened inside a Modal or Drawer, an overlay portals into it, so it always paints above its host

    #135
v0.6.0
  • Inputs drop the filled variant and the color prop

    #96
v0.0.1
  • The picker family: Select, MultiSelect, Autocomplete, Combobox, TreeSelect and TreeMultiSelect

  • Pickers open on a tap anywhere in the field, and their clear button is keyboard-reachable

On this page