Multi-select
Choose several values from a list. A self-contained convenience over Base UI Select's multiple mode: pass a flat options list and it renders the trigger, popup and checkable items. Selecting keeps the popup open; the trigger summarises the choice. Add searchable when the list needs a filter.
<MultiSelect options={[ { value: 'na', label: 'North America' }, { value: 'emea', label: 'EMEA' }, { value: 'apac', label: 'APAC' }, { value: 'latam', label: 'LATAM' }, ]} defaultValue={['na', 'emea']} placeholder="Select regions"/>With a label
<Label htmlFor="regions">Regions</Label><MultiSelect id="regions" options={regions} placeholder="Select regions" />Searchable
Pass searchable to add a filter input at the top of the popup. Options match against textValue, then a string label, then value. The empty-state text is customisable via emptyMessage.
<MultiSelect searchable options={countries} placeholder="Select countries" searchPlaceholder="Search countries"/>Select all
Pass selectAll for a toggle at the top of the list. It shows a check when everything is selected and a dash when only some are. Combined with searchable, it acts on the options matching the current query — filter, select-all, filter again, and earlier picks are kept.
<MultiSelect searchable selectAll options={countries} placeholder="Select countries" searchPlaceholder="Search countries"/>Custom items
Pass item to replace the popup row — a two-line name + key, an avatar, a badge — while label stays on the trigger. Set textValue so search still matches when the row is not plain text.
<MultiSelect searchable options={groups.map((group) => ({ value: group.value, label: group.name, textValue: `${group.name} ${group.key}`, item: ( <span className="flex flex-col gap-0.5"> <span className="font-medium text-primary">{group.name}</span> <span className="text-caption text-tertiary">{group.key}</span> </span> ), }))} placeholder="Add to groups" searchPlaceholder="Search name or key"/>Props
| Prop | Type | Default | Notes |
|---|---|---|---|
options | { value, label, item?, textValue?, disabled? }[] | — | The choices. `item` customises the popup row; `label` stays on the trigger; `textValue` is what search matches. |
value / defaultValue | string[] | — | Controlled or initial selection. |
onValueChange | (value: string[]) => void | — | Called with the new selection. |
placeholder | ReactNode | — | Shown when nothing is selected. |
formatCount | (count: number) => ReactNode | `${n} selected` | Summary when more than one is chosen. |
searchable | boolean | false | Adds a live-filtering search field at the top of the popup. |
searchPlaceholder | string | 'Search…' | Placeholder for the search field. |
emptyMessage | ReactNode | 'No results' | Shown when the query matches nothing. |
selectAll | boolean | false | Adds a select-all toggle; with searchable, acts on the filtered options. |
selectAllLabel | ReactNode | 'Select all' | Label for the select-all toggle. |
name / id / disabled | — | — | Standard form wiring. |