How to build an accessible CommandMenu in React
The search input is role="combobox" with aria-controls, aria-expanded, aria-autocomplete="list" and aria-activedescendant so navigation is announced without moving DOM focus from the input; it renders in a native <dialog> for focus trapping, the empty/loading state uses role="status", and footer hints, match highlights, status dots, and keycaps are aria-hidden as redundant decoration (status labels remain readable text). Scope pills are real buttons and the scope chip exposes an aria-labelled clear control
When to use a CommandMenu
- Giving power users a global Cmd/Ctrl+K palette to search and run actions or navigate
- Exposing many commands across grouped categories that benefit from fuzzy search
- Multi-step flows where selecting an item drills into a nested page of further commands
When not to use it
- Picking a form value from a list — use Combobox
- A small set of actions tied to a specific trigger button — use Dropdown
Keyboard interactions
Role combobox, verified at WCAG 2.2-AA.
Cmd/Ctrl+KArrowDownArrowUpHomeEndEnterCmd/Ctrl+EnterTabBackspaceEscape
Common mistakes
Avoid: Using CommandMenu as a form field to capture a selected value
Prefer: Use Combobox for value selection inside a form
CommandMenu is a modal command palette whose items run onSelect callbacks or open sub-pages; it has no controlled form value
Avoid: Items that set both page and an activation (onSelect/actions), or neither
Each item does one thing on Enter — run onSelect, run its first action, or push a nested page (page)
Example
<CommandMenu
open={open}
onOpenChange={setOpen}
groups={[
{
heading: 'Actions',
items: [
{ id: 'new', label: 'New file', shortcut: ['⌘', 'N'], onSelect: createFile },
{ id: 'search', label: 'Search docs', keywords: ['find'], onSelect: openSearch },
],
},
]}
/>