Combobox
Finds one or more values by typing.
Mode
singledefault
multiple
creatable
- . 1 invalid
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, filterBase UIcreatable, presetboolean, 'email'defaultfalsenormalize, validate, split, delimiters(raw) => string, (v, all) => string | null, (text) => string[], string[]defaulttrim, none, commas and lines, [',']pendingbooleandefaultfalseComboboxFielditems, multiple, placeholder, emptyText, size, showClear, showSearchIconComboboxInput size'sm' | 'default' | 'lg'default'default'ComboboxChips size'default' | 'lg'default'default'ComboboxContent size, anchor'default' | 'sm', Refdefault'default'ComboboxChip value, showRemovestring, booleandefault, 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