Pickers and overlays
Combobox
A text box that can also offer matching choices.
When to use it: Use it when people may type to narrow a long set of choices.
Example
import {
Combobox,
ComboboxInput,
ComboboxOptGroup,
ComboboxOption,
ComboboxPopover,
ComboboxTrigger,
Label,
} from "@comp0/react";
import { ChevronDownIcon } from "@heroicons/react/16/solid";
export function Example() {
return (
<Combobox as="div" className="flex max-w-xs flex-col gap-1.5" name="city">
<Label className="text-base font-medium text-zinc-900 sm:text-sm dark:text-zinc-100">
City
</Label>
<div className="flex">
<ComboboxInput
className="w-full rounded-l border border-r-0 border-zinc-950/10 bg-white px-3 py-2.5 text-base text-zinc-950 outline-teal-600 focus-visible:z-10 focus-visible:outline-2 sm:py-2 sm:text-sm dark:border-white/10 dark:bg-zinc-900 dark:text-zinc-50 dark:outline-teal-400"
placeholder="Filter cities"
/>
<ComboboxTrigger className="group rounded-r border border-zinc-950/10 bg-white px-3 text-zinc-500 outline-teal-600 focus-visible:z-10 focus-visible:outline-2 dark:border-white/10 dark:bg-zinc-900 dark:text-zinc-400 dark:outline-teal-400">
<ChevronDownIcon
className="size-4 transition-transform duration-150 ease-out group-data-open:rotate-180 motion-reduce:transition-none"
aria-hidden="true"
/>
</ComboboxTrigger>
</div>
<ComboboxPopover
placement="bottom"
offset={4}
className="max-h-60 w-[anchor-size(width)] max-w-[anchor-size(width)] overflow-y-auto rounded border-0 bg-white p-1 opacity-100 ring-1 ring-zinc-950/10 transition-[opacity,translate] duration-150 ease-out starting:-translate-y-1 starting:opacity-0 motion-reduce:transition-none dark:bg-zinc-900 dark:ring-white/10"
>
<ComboboxOptGroup label="Europe">
<div
aria-hidden="true"
className="px-3 pb-1 pt-2 text-xs font-medium text-zinc-500 dark:text-zinc-400"
>
Europe
</div>
<ComboboxOption
className="cursor-pointer rounded px-3 py-2.5 text-base text-zinc-800 hover:bg-zinc-100 data-active:bg-teal-100 data-active:text-zinc-950 data-selected:bg-teal-100 focus-visible:bg-teal-200 focus-visible:outline-2 focus-visible:outline-teal-600 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800 dark:data-active:bg-teal-950 dark:data-active:text-zinc-50 dark:data-selected:bg-teal-950 dark:focus-visible:bg-teal-800 dark:focus-visible:outline-teal-400"
value="Warsaw"
>
Warsaw
</ComboboxOption>
<ComboboxOption
className="cursor-pointer rounded px-3 py-2.5 text-base text-zinc-800 hover:bg-zinc-100 data-active:bg-teal-100 data-active:text-zinc-950 data-selected:bg-teal-100 focus-visible:bg-teal-200 focus-visible:outline-2 focus-visible:outline-teal-600 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800 dark:data-active:bg-teal-950 dark:data-active:text-zinc-50 dark:data-selected:bg-teal-950 dark:focus-visible:bg-teal-800 dark:focus-visible:outline-teal-400"
value="Lisbon"
>
Lisbon
</ComboboxOption>
</ComboboxOptGroup>
<ComboboxOptGroup label="Asia">
<div
aria-hidden="true"
className="px-3 pb-1 pt-2 text-xs font-medium text-zinc-500 dark:text-zinc-400"
>
Asia
</div>
<ComboboxOption
className="cursor-pointer rounded px-3 py-2.5 text-base text-zinc-800 hover:bg-zinc-100 data-active:bg-teal-100 data-active:text-zinc-950 data-selected:bg-teal-100 focus-visible:bg-teal-200 focus-visible:outline-2 focus-visible:outline-teal-600 sm:py-2 sm:text-sm dark:text-zinc-100 dark:hover:bg-zinc-800 dark:data-active:bg-teal-950 dark:data-active:text-zinc-50 dark:data-selected:bg-teal-950 dark:focus-visible:bg-teal-800 dark:focus-visible:outline-teal-400"
value="Tokyo"
>
Tokyo
</ComboboxOption>
</ComboboxOptGroup>
</ComboboxPopover>
</Combobox>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
Combobox
Selected-value, open-state, and form-serialization provider. Does not add a DOM element.
Label
Visible name connected to the input. Owns a DOM element.
ComboboxInput
Native text input that owns editing and required validity. Owns a DOM element.
ComboboxTriggerOptional
Optional button that opens or closes the suggestions with a pointer. Owns a DOM element.
ComboboxPopover
The listbox results surface. Owns a DOM element.
ComboboxOptGroupOptional
Optional native-style group of related results. Owns a DOM element.
ComboboxOption
Selectable result. Owns a DOM element.
Step by step
- 1
Add the main part
Start Combobox with a labelled ComboboxInput.
- 2
Add the supporting parts
Place ComboboxTrigger beside the input, then add ComboboxPopover as the listbox of results; group related results with ComboboxOptGroup.
- 3
Make the behavior clear
Combobox owns the selected value, field, form serialization, and open state; control the results with open, defaultOpen, and onToggle.
Exampletsx <Combobox name="city"><Label>City</Label><ComboboxInput /><ComboboxTrigger aria-label="Show suggestions" /><ComboboxPopover><ComboboxOption value="Paris">Paris</ComboboxOption></ComboboxPopover></Combobox>
Keyboard
- ↓
- Moves through options.
- ↵
- Selects the active option from the input or opens from ComboboxTrigger.
- Space
- Opens suggestions from ComboboxTrigger.
- Esc
- Closes results.
Forms and accessibility
Combobox.name owns selected-value serialization. ComboboxInput owns text editing and required validity.
Accessibility checklist
- Use a visible Label so the input and results list share a clear name; without one, name both parts explicitly.
- Give every ComboboxOptGroup a native label so grouped results have a name.
- Keep option text specific enough to distinguish matches.
- Do not hide required instructions only in the result list.
API reference
import { Combobox, ComboboxInput, ComboboxOptGroup, ComboboxOption, ComboboxPopover, ComboboxTrigger, Label } from "@comp0/react";Combobox
Context onlySelected-value, open-state, and form-serialization provider.
| Prop | Type | Description |
|---|---|---|
value | string | Controlled committed option. |
defaultValue | string | Initial committed option. |
onChange | (value: string) => void | Receives the committed option. |
inputValue / defaultInputValue | string | Controlled or initial editable text. |
onInputChange | (value: string) => void | Receives the editable text. |
open / defaultOpen | boolean | Controlled or initial open state of the results. |
onToggle | (open: boolean) => void | Receives the next open state. |
filter | (textValue, inputValue) => boolean | Custom match rule for results. |
name | string | Submission name. |
form | string | Associates the combobox value with a form by id. |
Label
DOM elementVisible name connected to the input.
ComboboxInput
DOM elementNative text input that owns editing and required validity.
| Prop | Type | Description |
|---|---|---|
placeholder | string | Hint text; never a replacement for Label. |
aria-label | string | Names the input when there is no visible Label. |
disabled / required | boolean | Override the field-wide state for this control. |
ComboboxTrigger
OptionalDOM elementOptional button that opens or closes the suggestions with a pointer.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Names the button; defaults to the English "Show suggestions". |
disabled | boolean | Disables the trigger and inherits Combobox.disabled. |
ComboboxPopover
DOM elementThe listbox results surface.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Names the results list when there is no visible Label. |
placement | PopoverPlacement | Input side to open on, such as "bottom"; flips when there is no room. |
offset | number | Pixel gap between the input and the listbox. |
ComboboxOptGroup
OptionalDOM elementOptional native-style group of related results.
| Prop | Type | Description |
|---|---|---|
label | string | Names the group before its results. |
ComboboxOption
DOM elementSelectable result.
| Prop | Type | Description |
|---|---|---|
value | string | This result’s value. |
disabled | boolean | Disables the result. |
textValue | string | Overrides the text crawled from children when markup makes it ambiguous. |
Style hooks
Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.
[data-open]on ComboboxInput, ComboboxTrigger, ComboboxPopover
- Results are open.
:popover-openon ComboboxPopover
- Native pseudo-class equivalent.
[data-selected]on ComboboxOption
- Option is selected.
[data-active]on ComboboxOption
- Option is active and receives the visible keyboard highlight.
[aria-activedescendant]on ComboboxInput
- The input points to the keyboard-active option while focus stays in the input.