Inputs / Select & MultiSelect
Select & MultiSelect
Pick one or pick many from a taped paper sheet. The trigger has the fields' wobbly marker outline and boils while open, picks get a tick that draws itself in, and in a MultiSelect every pick sticks onto the trigger as washi tape. Add a search box for long lists, like a people picker.
Client componentWCAG AANew
Brush
Paper
Collaborator
Tags
Sort by
Paper
Pick a paper before you post.
Brush
Locked while the sketch is shipping.
Collaborator
import { MultiSelect, Select } from "@plop-ui/react";
<Select
label="Brush"
options={[
{ value: "pencil", label: "Pencil", hint: "Light and scratchy", lead: { dot: "periwinkle" } },
{ value: "marker", label: "Marker", hint: "Bold and a bit wobbly", lead: { dot: "mustard" } },
]}
value={brush}
onValueChange={setBrush}
/>
// A people picker: searchable, grouped, with doodle faces
<Select label="Collaborator" searchable searchLabel="Find a friend" options={friends} />
// friends: [{ value: "rosa", label: "Rosa Santos", hint: "Illustrator", group: "Online", lead: { avatar: "Rosa Santos", status: "online" } }, …]
<MultiSelect label="Tags" options={tags} value={picked} onValueChange={setPicked} />
// tags: [{ value: "wild", label: "Wild idea", tone: "tomato" }, …] each pick becomes a strip of tape
<Select label="Paper" options={papers} error="Pick a paper before you post." />
<Select label="Collaborator" options={[]} loading />Change every prop live in the playground.
Props
Select
| Prop | Type | Default | What it does |
|---|---|---|---|
| label required | ReactNode | — | The visible label. Required: every field needs a name. |
| options required | SelectOption[] | — | The choices, in order: `value`, `label`, and optional `hint`, `group`, `lead`, `tone`, `disabled`. |
| className | string | none | Extra class on the field wrapper. |
| defaultValue | string | null (nothing picked) | Uncontrolled initial value. |
| disabled | boolean | false | Greys out the trigger so the list can't be opened. |
| error | ReactNode | none | An error that says how to fix it. Turns the outline tomato; it's read as the trigger's description. |
| hint | ReactNode | none | Help under the field. |
| loading | boolean | false | While options are on their way: a skeleton line in the trigger, hopping dots instead of the chevron, and the trigger waits (aria-busy, can't open). |
| onValueChange | (value: string) => void | none | Called with the picked option's value. |
| placeholder | string | "Choose…" | Shown while nothing is picked. |
| searchable | boolean | false | Adds a search box at the top of the list that filters by label and hint. |
| searchLabel | string | "Search" | The search box's label and placeholder. |
| size | "sm" | "md" | none | `md` (default) or `sm` for toolbars. |
| value | string | none | Controlled picked value; `null` means nothing is picked. |
MultiSelect
| Prop | Type | Default | What it does |
|---|---|---|---|
| label required | ReactNode | — | The visible label. Required: every field needs a name. |
| options required | SelectOption[] | — | The choices, in order: `value`, `label`, and optional `hint`, `group`, `lead`, `tone`, `disabled`. |
| className | string | none | Extra class on the field wrapper. |
| defaultValue | string[] | [] | Uncontrolled initial values. |
| disabled | boolean | false | Greys out the trigger so the list can't be opened. |
| error | ReactNode | none | An error that says how to fix it. Turns the outline tomato; it's read as the trigger's description. |
| hint | ReactNode | none | Help under the field. |
| loading | boolean | false | While options are on their way: a skeleton line in the trigger, hopping dots instead of the chevron, and the trigger waits (aria-busy, can't open). |
| onValueChange | (value: string[]) => void | none | Called with the full new list of picked values each time one is added or removed. |
| placeholder | string | "Choose…" | Shown while nothing is picked. |
| searchable | boolean | false | Adds a search box at the top of the list that filters by label and hint. |
| searchLabel | string | "Search" | The search box's label and placeholder. |
| size | "sm" | "md" | none | `md` (default) or `sm` for toolbars. |
| value | string[] | none | Controlled picked values. |
Use it well
DoUse it for 6 or more choices, or when space is tight. Turn on searchable once a list gets long enough to scroll.
Don'tHide 2 to 5 choices people should compare at a glance; a RadioGroup shows them all at once.
Accessibility
- The trigger is a button with aria-haspopup="listbox"; the sheet is a listbox of options with aria-selected, and aria-multiselectable in a MultiSelect.
- Arrows move (disabled options are skipped), Home and End jump, a typed letter jumps, Enter or Space picks, Escape closes and puts focus back on the trigger, Tab closes.
- Picked is never color alone: a tick, or a ticked box, draws in. Errors are read as the trigger's description.
- With searchable on, a combobox at the top keeps focus while arrows move through the matches.