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: pipelines, tables, queues, workers.
Use
Several values from one list, shown together (multiple).
Use
Values the list cannot know: new labels, the emails paged when a job fails (multiple creatable).
Not for
Seven or fewer options known up front. Use Select.
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 "ops@" commits as a valid tag.
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 tag 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 tags 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 itself, white with the gold edge doubled inward and a 1px pale halo, as Input does, and a ring outside that reads as a second box.

State order

6 states, highest first
1
disabled
2
invalid
3
focus
4
pending
5
hover
6
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 tag says its reason in title and in screen reader text, and the field turns red, so colour is never the only cue.
Each tag's remove button reads Remove and the value; the caret reads Show options and stays out of the tab order.
Focus turns the field white with its edge --focus and a pale halo; inside the list the highlighted row lifts to white between two hairlines, and a chosen row carries an ink check, no fill.

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 tags field, removes the last tag.
Left
At the start of the text, moves into the tags.

Edge cases

8 edge cases
Long tag
Truncates inside the field with an ellipsis.
Twenty-four tags
They wrap; the field stops at four rows and scrolls.
Pasted list
Splits into tags; a quoted "Dee, Ops" <dee@acme.example> stays one address.
Value typed twice
Adds once; the text clears and the existing tag flashes the selection 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
Tags 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

33 tokens
--field-bg
#f8f7f3
--field-hover
#fefefd
--field-focus-bg
#ffffff
--field-ring-focus
#fff1b8
--field-ring-fail
#fbdcd8
--field-inset
10px
--row-highlight
#ffffff
--row-highlight-edge
var(--line)
--well
#f4f3ee
--selected
#fff4bf
--line
#e2dfd5
--focus
#7a5d00
--fail
#b3261e
--fail-text
#b3261e
--fail-tint
#fdecea
--hover
rgb(22 21 18 / 0.04)
--text-1
#161512
--text-2
#55524a
--text-3
#6b675e
--control-sm
26px
--control
32px
--control-lg
36px
--row-sm
32px
--menu-min
192px
--popup-max
320px
--icon
16px
--icon-sm
14px
--pending-cell
3px
--type-body-sm
400 14px/20px var(--font-sans)
--type-code-sm
400 12px/16px var(--font-mono)
--type-label-sm
400 12px/16px var(--font-mono)
--type-caption
400 13px/18px var(--font-sans)
--dur-state
120ms

Install

For your coding agent

GitHub

Please star me pookie

198

A new design system every week

Leave your email before you copy the prompt.