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"? UseAutocomplete/MultiSelect/Select.Comboboxis the composition layer underneath.
Playground
Combobox
Dir
Presets
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
| Part | Wraps (Ark) | Notes |
|---|---|---|
Combobox.Root | Combobox.Root | Takes 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.Control | Combobox.Control | The 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.Input | Combobox.Input | The search <input>. Accepts testId, landing on this <input>. |
Combobox.Trigger | Combobox.Trigger | Chevron toggle; rotates when open. |
Combobox.ClearTrigger | Combobox.ClearTrigger | Clears the value; auto-hidden when empty. |
Combobox.Content | Positioner + Content | The floating panel (positioner + scroll container in one). |
Combobox.Item | Item + ItemText + ItemIndicator | An option. Pass item={collectionItem}; renders a check when selected. Accepts testId, landing on this option's element. |
Combobox.Group | ItemGroup + ItemGroupLabel | Pass label to head a group of options. |
Combobox.Empty | Empty | Shown when the collection is empty (e.g. no matches). |
Combobox.Context | Combobox.Context | Render-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,Enterto select,Escto close,Home/Endto jump. role="combobox"/listbox/optionand thearia-activedescendant/aria-expandedwiring 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 throughCombobox.Root.
Changelog
- #251
An outside click no longer dismisses the parent while a nested overlay is open
- #135
Opened inside a
ModalorDrawer, an overlay portals into it, so it always paints above its host
- #96
Inputs drop the
filledvariant and thecolorprop
The picker family:
Select,MultiSelect,Autocomplete,Combobox,TreeSelectandTreeMultiSelectPickers open on a tap anywhere in the field, and their clear button is keyboard-reachable
Rating
Fixed-count star rating on top of Ark UI's RatingGroup machine — half-star precision, sizes, colors, and custom symbols, with the full keyboard / ARIA model handled upstream.
Select
Data-driven single-value picker on Ark UI's Select machine — pass data, get a styled trigger, dropdown, native form submission, typeahead, and full keyboard / RTL / ARIA.