MultiSelect

Searchable multi-value select with a popover listbox, chips and grouping

Category: inputs · WCAG 2.2-AA · form, select, multi, input, popover, combobox, tags

Sizes

States

Props

PropTypeDefaultDescription
optionsMultiSelectOption[]The selectable options.
valuestring[]The controlled value. Omit it and pass defaultValue to let the component own the selection.
defaultValuestring[][]The initial value when uncontrolled.
onValueChange(value: string[]) => voidCalled with the new value when it changes.
placeholderstringPlaceholder text shown when the field is empty.
labelstringVisible field label, rendered above the trigger.
ariaLabelstringInvisible accessible name, for when a visible element outside this component already labels it and `label` would render that text a second time. ⚠ `label` on this component is **visible**; `IconButton.label`/`Sparkline.label` are invisible names, which is the prior that costs adopters a duplicated label.
aria-labelledbystringWired automatically by a wrapping `Field` — its label id, forwarded to the focusable control so the Field's label names it.
aria-describedbystringWired automatically by a wrapping `Field` — the ids of its hint/error text, forwarded to the focusable control so the supporting text is announced.
aria-invalidbooleanWired automatically by a wrapping `Field` when it is in an error state.
hintstringHelper text below the field.
errorstringError text below the field; also marks the control invalid.
display'count' | 'chips''count'How the trigger summarises the selection: a count, or one removable chip per value.
disabledbooleanfalseWhen true, disables the control and removes it from the tab order.
clearablebooleanfalseWhen true, shows a control that clears every selected value.
selectAllbooleanfalseWhen true, shows a row that selects or clears every enabled option at once.
maxnumberMaximum number of values that may be selected. Further options become unselectable once reached.
creatablebooleanfalseWhen true, offers the current search text as a new option.
onCreate(label: string) => voidCalled with the typed label when the user picks the "create" row.
loadingbooleanfalseWhen true, the list reports itself as busy and shows a loading row instead of the no-results message.
onSearchChange(query: string) => voidCalled with the search text on every keystroke. Pair it with filter={() => true} for a server-driven list.
filter(option: MultiSelectOption, query: string) => booleanReplaces the built-in diacritic-insensitive matcher.
searchablebooleantrueWhen true, shows the search field. The list is keyboard-navigable either way.
size'sm' | 'md' | 'lg''md'Field height.
namestringSubmitted with a surrounding form — one hidden input per selected value.
labelsMultiSelectLabelsOverrides for the component’s user-visible strings (i18n).
idstringId for the trigger control.

Design tokens

When to use

When not to use

How to build an accessible MultiSelect in React →

Examples

Basic

<MultiSelect options={[{label:'One',value:'1'},{label:'Two',value:'2'}]} defaultValue={[]} />

Chips with a clear control

Each selection renders as a chip with its own remove button.

<MultiSelect options={options} display="chips" clearable defaultValue={['1']} />

Grouped options

Options carrying a group render under a labelled role="group" heading.

<MultiSelect options={[{label:'Apple',value:'a',group:'Pome'},{label:'Cherry',value:'c',group:'Stone'}]} />

Remote search

filter={() => true} hands filtering to the server; loading marks the list busy between keystroke and response.

<MultiSelect options={results} loading={pending} onSearchChange={search} filter={() => true} />

Bounded selection

Options past the limit report aria-disabled; select-all stops at the limit.

<MultiSelect options={options} max={3} selectAll />

Related components

← Back to docs