How to build an accessible MultiSelect in React
The trigger advertises aria-haspopup="listbox" and aria-expanded, the panel is role="listbox" with aria-multiselectable, each option is role="option" with aria-selected reflecting membership and aria-disabled for unavailable ones; ArrowUp/ArrowDown move the active option, Space/Enter toggle it, and Escape closes the popover.
When to use a MultiSelect
- Selecting several values at once from a known list of options
- Lists long enough that the built-in search/filter helps the user find options
- Cases needing a compact trigger that summarizes the selected count
When not to use it
- Choosing exactly one value — use Select
- Allowing free-text entries not in a predefined list — use Combobox or TagsInput
- A handful of always-visible options — use a Checkbox group
Keyboard interactions
Role listbox, verified at WCAG 2.2-AA.
ArrowDownArrowUpSpaceEnterEscape
Common mistakes
Avoid: <MultiSelect value={value} onValueChange={...} /> without keeping value in sync
Prefer: Store the selected string[] and update it from onValueChange every toggle
Selection is fully controlled; the listbox reflects value, so dropping the update leaves checkboxes out of sync with state
Example
<MultiSelect options={[{label:'One',value:'1'},{label:'Two',value:'2'}]} value={[]} onValueChange={() => {}} />