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

When not to use it

Keyboard interactions

Role listbox, verified at WCAG 2.2-AA.

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={() => {}} />

See the full MultiSelect reference →