Combobox
A text field that filters a list as you type. Pick one value, or many as chips. Use it when a list is too long to scan, such as teammates, channels, or time zones. For a short fixed list, use Select.
Priya Raman · #launches, #support-escalations
Usage
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@tulipfarm/sprout/combobox";
const fruits = ["Apple", "Apricot", "Blueberry", "Cherry", "Pear"];
<Combobox items={fruits}>
<ComboboxInput aria-label="Choose a fruit" placeholder="Choose a fruit" showClear />
<ComboboxContent>
<ComboboxEmpty>No fruit found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>Pass items to Combobox and a function to ComboboxList. The list filters as you type, and ComboboxEmpty shows when nothing matches. For objects, pass itemToStringLabel andisItemEqualToValue.
Single, multiple, and disabled
These are the three forms TulipFarm uses: a single choice with a clear button, many choices as chips with a starting value, and a disabled field.
Input options
ComboboxInput shows a chevron by default. Turn it off with showTrigger={false} so the list opens on typing or ↓. showClear adds a clear button that replaces the chevron once there is a value. Children are added to the group, such as a leading icon.
Chips
With multiple, render ComboboxChips from ComboboxValue inside ComboboxChips, then a ComboboxChipsInput. Pass a ref from useComboboxAnchor to the chips and to ComboboxContent’s anchor so the list matches the field. Set showRemove={false} on a chip that must stay. Chips wrap when they don’t fit.
const anchor = useComboboxAnchor();
<Combobox items={channels} multiple defaultValue={["launches"]}>
<ComboboxChips ref={anchor}>
<ComboboxValue>
{(values: string[]) =>
values.map((value) => <ComboboxChip key={value}>{value}</ComboboxChip>)
}
</ComboboxValue>
<ComboboxChipsInput aria-label="Alert channels" placeholder="Add channel" />
</ComboboxChips>
<ComboboxContent anchor={anchor}>
<ComboboxEmpty>No channel found.</ComboboxEmpty>
<ComboboxList>
{(channel) => <ComboboxItem key={channel} value={channel}>{channel}</ComboboxItem>}
</ComboboxList>
</ComboboxContent>
</Combobox>Trigger button
ComboboxTrigger can be the whole control. Render it as a Button, show ComboboxValue inside, and put a ComboboxInput with showTrigger={false} at the top of the list. With no children it is an icon button named “Show options”.
Groups and separators
Pass groups as items. Render each one as a ComboboxGroup with its own items, a ComboboxLabel, and aComboboxCollection. Put a ComboboxSeparatorbetween groups.
States
While items load, pass an empty list, set aria-busy on the input, and say so in ComboboxEmpty. Set aria-invalid and point aria-describedby at the error. Disabled items can be reached with the arrow keys but can’t be chosen. A disabled combobox can’t be focused.
Loading tables…
No tables yet. Create one in Tables first.
Locked while the workflow runs.
API
Combobox is the Base UI combobox root. It takes items, multiple, value, defaultValue,onValueChange, itemToStringLabel, disabled, name, and every other root prop.
ComboboxInput takes showTrigger (true), showClear (false), disabled, and every input prop. Its className goes on the InputGroup.
ComboboxContent renders its own portal and positioner. It takesside (bottom), align (start), sideOffset (6), alignOffset (0), and anchor.
ComboboxChip takes showRemove (true). ComboboxTrigger, ComboboxValue, ComboboxList, ComboboxItem, ComboboxGroup, ComboboxLabel, ComboboxCollection, ComboboxEmpty, ComboboxSeparator, ComboboxChips, and ComboboxChipsInput wrap the Base UI parts and take their props. useComboboxAnchor returns a ref for the chips. Every part sets a data-slot.
Accessibility
The input is a combobox with aria-expanded and aria-autocomplete. Typing or ↓ opens the list. Arrow keys move, Enter picks, and Escape closes and keeps focus in the input. In a chips field, ← and → move between chips and Backspace removes one. Each remove button is named “Remove” plus the chip text. ComboboxEmpty is a polite live region.
The list fades and scales in over 120ms. Motion turns off when reduced motion is requested. On touch screens items are at least 44px tall. In forced-colors mode the highlighted item gets an outline.
Theme tokens
Comboboxes use --sprout-surface, --sprout-canvas, --sprout-input, --sprout-line, --sprout-hover, --sprout-secondary, --sprout-text, --sprout-muted, --sprout-focus, --sprout-danger, --sprout-shadow-overlay, and the control and panel radii. Only the focus ring follows the brand, so they work with the neutral and TulipFarm themes in light and dark mode.