Form
MultiSelect
Multi-value select with removable chips in the trigger and checkbox-style options; the popup stays open while toggling values.
Import
import { MultiSelect } from "@zephora/react";Examples
Basic
Choose toppings…
<MultiSelect
aria-label="Toppings"
placeholder="Choose toppings…"
options={[
{ label: "Cheese", value: "cheese" },
{ label: "Mushrooms", value: "mushrooms" },
{ label: "Olives", value: "olives" },
{ label: "Pineapple", value: "pineapple", disabled: true },
]}
/>Controlled
React
Selected: react
const [tags, setTags] = React.useState<string[]>(["react"]);
<MultiSelect
aria-label="Tags"
value={tags}
onValueChange={setTags}
options={[
{ label: "React", value: "react" },
{ label: "TypeScript", value: "ts" },
{ label: "CSS", value: "css" },
{ label: "Testing", value: "testing" },
]}
/>
<p>Selected: {tags.join(", ") || "none"}</p>Chip overflow
`maxDisplay` collapses chips beyond the given count into a +N chip.
AnaBen+2
<MultiSelect
aria-label="Team"
maxDisplay={2}
defaultValue={["ana", "ben", "cem", "eda"]}
options={[
{ label: "Ana", value: "ana" },
{ label: "Ben", value: "ben" },
{ label: "Cem", value: "cem" },
{ label: "Eda", value: "eda" },
]}
/>Search, clear and a selection limit
`searchable` adds a filter input inside the popup, `clearable` shows a clear-all button, and `maxCount` disables unselected options once the limit is reached. `renderValue` replaces the chips with your own trigger content.
Select…
<MultiSelect
aria-label="Skills"
searchable
clearable
maxCount={3}
renderValue={(selected) => <>{selected.length} of 3 skills</>}
options={[
{ label: "React", value: "react" },
{ label: "TypeScript", value: "ts" },
{ label: "Node.js", value: "node" },
{ label: "GraphQL", value: "graphql" },
{ label: "Rust", value: "rust" },
]}
/>API
MultiSelect props
| Prop | Type | Default | Description |
|---|---|---|---|
options * | MultiSelectOption[] | — | Options shown in the listbox. Same shape as SelectOption (label, value, disabled?, icon?, description?, group?, textValue?). |
value | string[] | — | Controlled selected values. |
defaultValue | string[] | [] | Initial values in uncontrolled mode. |
onValueChange | (value: string[]) => void | — | Called with the new value array on every change. |
placeholder | string | "Select…" | Text shown when nothing is selected. |
maxDisplay | number | — | Collapses chips beyond this count into "+N". |
maxCount | number | — | Selection limit — once reached, unselected options are disabled. |
searchable | boolean | false | Shows a search input in the popup that filters options client-side. |
clearable | boolean | false | Shows a clear-all button while something is selected. |
renderValue | (selected: MultiSelectOption[]) => ReactNode | — | Custom trigger value renderer — replaces the chips when something is selected. |
size | "sm" | "md" | "lg" | "md" | Trigger scale. |
invalid | boolean | false | Marks the trigger invalid (aria-invalid). |
disabled | boolean | false | Disables the trigger. |
aria-label / aria-labelledby | string | — | Accessible name for the trigger and listbox. |
unstyled | boolean | false | Headless mode — drops Zephora classes so your own CSS can style it. |
…rest | HTMLAttributes<HTMLDivElement> | — | Forwarded to the root wrapper element. |
Keyboard
| Key | Action |
|---|---|
Enter / Space / Arrow Down / Arrow Up | Opens the listbox from the trigger. |
Arrow Down / Arrow Up | Moves the active option (skips disabled). |
Enter / Space | Toggles the active option; the popup stays open. |
Backspace | Removes the last selected value from the trigger. |
Escape | Closes the listbox. |
A-Z | Typeahead — jumps to the next matching option. |