Combobox

Finds one or more values by typing.

Mode

  • singledefault

  • multiple

  • creatable

  • email

Sizes

  • sm

  • defaultdefault

  • lg

States

  • rest

  • focus

  • filled

  • invalid

  • disabled

  • pending

  • open

  • empty

  • searching

  • create row

Usage

When to use it
Use
More than about seven options, so typing beats scanning: agents, models, queues, tools.
Use
Several values from one list, shown together (multiple).
Use
Values the list cannot know: new labels, the emails allowed to review a run (multiple creatable).
Not for
Seven or fewer options known up front. Use Select.
Not for
A search across the product that runs commands. Use Command.
Not for
One line of free text with no list. Use Input.

Rules

9 rules
Must
Name the field with a Field and FieldLabel, or aria-label. Because a placeholder is not a name and goes on the first keystroke, so a screen reader hears only "combobox".
Must
Pass pending while results load, and filter={null} on Combobox when the server filters. Because without pending the list says No matches while results load, and the client filter drops and reorders server matches.
Must
Give multiple creatable a validate or a preset when the format matters. Because without one a typo like "ana@" commits as a valid chip.
Should
Name what was searched in emptyText.
Should
Set showClear on an optional single field. Because once a value is picked there is no other way back to empty.
Must
Pass the value as a plain string child of ComboboxChip. Because the remove button takes its name from string children only, so a chip holding an element gets a button named just "Remove".
Never
Use a combobox for seven options or fewer. Because typing to filter a list that already fits on screen is an extra step, and Select shows every option at once.
Never
Lift the chips field's height cap with className. Because a pasted list of two hundred emails would grow the field and push the rest of the form off the screen; it stops at four rows and scrolls.
Never
Draw a ring or a shadow on the field to show focus or the open list. Because the field shows focus by darkening its own line, as Input does.

State order

5 states, highest first
1
disabled
2
invalid
3
focus
4
pending
5
rest

Accessibility

5 checks
The text is role="combobox" with aria-expanded, aria-controls, aria-activedescendant and aria-invalid; the list is a listbox, multiselectable with multiple.
Adds, removes and Searching are announced in polite status regions.
A marked chip says its reason in title and in screen reader text, and the field turns coral, so colour is never the only cue.
Each chip's remove button reads Remove and the value; the caret reads Show options and stays out of the tab order.
Focus darkens the pill's line to --line-focus; the highlighted row's wash is the focus inside the list.

Keyboard

5 keys
Down, Up
Opens the list and moves the highlight.
Enter
Picks the highlighted row, or adds the typed value when creatable.
Escape
Closes the list; a second Escape reaches the dialog around it.
Backspace
In the empty text of a chips field, removes the last chip.
Left
At the start of the text, moves into the chips.

Edge cases

8 edge cases
Long chip
Truncates inside the field with an ellipsis.
Twenty-four chips
They wrap; the field stops at four rows and scrolls.
Pasted list
Splits into chips; a quoted "Dee, Ops" <dee@acme.example> stays one address.
Value typed twice
Adds once; the text clears and the existing chip flashes the pressed wash.
items=[] and not creatable
The empty text; with creatable, nothing until the person types.
Slow search
Keeps the Searching row until results arrive.
Inside a form
Enter picks or adds and does not submit while the list is open.
Right to left
Chips flow right to left; the check and the caret swap sides.

API

9 props
Combobox items, value, defaultValue, onValueChange, multiple, filter
Base UI
creatable, preset
boolean, 'email' default false
normalize, validate, split, delimiters
(raw) => string, (v, all) => string | null, (text) => string[], string[] default trim, none, commas and lines, [',']
pending
boolean default false
ComboboxField
items, multiple, placeholder, emptyText, size, showClear, showSearchIcon
ComboboxInput size
'sm' | 'default' | 'lg' default 'default'
ComboboxChips size
'default' | 'lg' default 'default'
ComboboxContent size, anchor
'default' | 'sm', Ref default 'default'
ComboboxChip value, showRemove
string, boolean default , true

Tokens

26 tokens
--paper
#ffffff
--field
#efefef
--line-control
rgb(5 5 5 / 0.15)
--line-focus
rgb(5 5 5 / 0.32)
--line
rgb(5 5 5 / 0.10)
--fail
#e35e49
--fail-text
#b23c29
--hover
rgb(5 5 5 / 0.06)
--pressed
rgb(5 5 5 / 0.10)
--text-1
rgb(5 5 5 / 0.80)
--text-2
rgb(5 5 5 / 0.60)
--text-3
rgb(5 5 5 / 0.45)
--control-sm
26px
--control
32px
--control-lg
36px
--shell-inset
3px
--menu-row
32px
--menu-min
192px
--popup-max
320px
--radius-pill
999px
--radius-overlay
10px
--radius-tag
6px
--type-body-sm
400 13px/1.5 var(--font-sans)
--type-label
500 13px/1.35 var(--font-sans)
--type-caption
400 12px/1.35 var(--font-sans)
--dur-state
160ms

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.