CommandMenu
Unstable: a searchable list of places and actions in a dialog, opened with ⌘K (Ctrl+K elsewhere) or a button, as Figma's Command Menu draws it. Import it from @nizivo/react/unstable/command-menu.
Import
import { CommandMenu } from '@nizivo/react/unstable/command-menu';
// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css and @nizivo/react/css/dialog.css and @nizivo/react/css/kbd.css
import '@nizivo/react/css/command-menu.css';Unstable
It ships at @nizivo/react/unstable/command-menu, outside the semver promise. Still to come:
results that load as the reader types (async sources), a cap per group, a long list that renders
only what shows, a spoken count of the results, and loading the menu only on the first ⌘K.
Static or interactive
Client. It is the Dialog with React Aria's Autocomplete inside. The server renders the trigger; the dialog renders only after hydration, when it opens. How to choose.
Parts
CommandMenu.Root holds whether it is open and registers its shortcut (hotkey, 'mod+k' by
default; null for none). CommandMenu.Trigger opens it from the pointer and touch, and names the
shortcut through aria-keyshortcuts. CommandMenu.Popup is the dialog; in it, CommandMenu.Input
(the search row, with an "esc" hint), CommandMenu.List of CommandMenu.Groups with a heading and
their CommandMenu.Items, and CommandMenu.Footer, the keys as Figma shows them.
Rows
An item is a link (href) or an action (onAction, in a client component); choosing it closes the
menu. icon draws a 20 px mark before its text, kind says what it is at the row's end ("Product",
"Order"), and shortcut draws its own Kbd and announces it. The search matches textValue, or the
item's text when it is a string. filter on the Popup is 'contains' (default) or 'startsWith'.
Shortcuts
The menu's shortcut is registered in @nizivo/react/hotkeys, one list for the whole page that a
help dialog can read with useHotkeys. useHotkey and registerHotkey add your own: they never
fire during an IME composition or while the reader types in a field, unless allowInInputs, and a
single printable key with no modifier warns in development (WCAG 2.1.4).
Examples
Basic
Figma's command menu: products, an order and actions, filtered as you type. Press ⌘K (Ctrl+K elsewhere) or the button; the arrow keys move, Enter opens the row, Escape clears the search and then closes.
import type { JSX } from 'react';
import { Button } from '@nizivo/react/button';
import { CommandMenu } from '@nizivo/react/unstable/command-menu';
/** Stand-ins for `@nizivo/icons` glyphs. */
function Box(): JSX.Element {
return (
<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor">
<path d="M4 7l8-4 8 4v10l-8 4-8-4z" />
</svg>
);
}
function Sheet(): JSX.Element {
return (
<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor">
<path d="M6 3h12v18H6zM9 8h6M9 12h6" />
</svg>
);
}
function Plus(): JSX.Element {
return (
<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor">
<path d="M12 5v14M5 12h14" />
</svg>
);
}
export function CommandMenuExample(): JSX.Element {
return (
<CommandMenu.Root>
<CommandMenu.Trigger render={<Button variant="secondary">Search…</Button>} />
<CommandMenu.Popup>
<CommandMenu.Input placeholder="Search products, orders and actions" />
<CommandMenu.List>
<CommandMenu.Group heading="Products">
<CommandMenu.Item href="#stoneware-mug" icon={<Box />} kind="Product">
Stoneware mug
</CommandMenu.Item>
<CommandMenu.Item href="#stoneware-bowls" icon={<Box />} kind="Product">
Stoneware bowl set
</CommandMenu.Item>
</CommandMenu.Group>
<CommandMenu.Group heading="Orders">
<CommandMenu.Item href="#order-1042" icon={<Sheet />} kind="Order">
#1042 · Stoneware mug ×2
</CommandMenu.Item>
</CommandMenu.Group>
<CommandMenu.Group heading="Actions">
<CommandMenu.Item href="#new-product" icon={<Plus />} kind="Action" shortcut="mod+n">
Create product
</CommandMenu.Item>
<CommandMenu.Item
href="#new-discount"
icon={<Plus />}
kind="Action"
shortcut="mod+shift+d"
>
Create discount
</CommandMenu.Item>
</CommandMenu.Group>
</CommandMenu.List>
<CommandMenu.Footer />
</CommandMenu.Popup>
</CommandMenu.Root>
);
}
Parts
Use the parts through the namespace (CommandMenu.Root) or by their flat names (CommandMenuRoot); both work from Server Components.
CommandMenu.Root
Client component Renders no element of its own. Props: CommandMenuRootProps. Source: src/unstable/command-menu/command-menu.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
childrenrequired | ReactNode | – | |
defaultOpen | boolean | – | |
hotkey | string | null | 'mod+k' | The shortcut that opens and closes it, as Kbd writes it; |
labels | Partial<CommandMenuLabels> | – | |
onOpenChange | (open: boolean) => void | – | |
open | boolean | – |
CommandMenu.Trigger
Client component Renders no element of its own. Props: CommandMenuTriggerProps. Source: src/unstable/command-menu/command-menu.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | – | |
render | RenderProp<DialogTriggerState> | – | Must render a |
- Also takes every attribute of
<button>exceptchildren. (Omit<ComponentPropsWithRef<"button">, "children">)
render takes an element, or, in client code, a function of the props and this state (DialogTriggerState):
| Name | Type | Default | Description |
|---|---|---|---|
disabledrequired | boolean | – | |
focusVisiblerequired | boolean | – | |
openrequired | boolean | – | |
pressedrequired | boolean | – |
CommandMenu.Popup
Client component Renders no element of its own. Props: CommandMenuPopupProps. Source: src/unstable/command-menu/command-menu.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
childrenrequired | ReactNode | – |
|
className | string | – | |
filter | CommandMenuFilter"contains""startsWith" | 'contains' |
CommandMenu.Input
Client component Renders data-slot="command-menu-input" with the class nz-command-menu-input. Props: CommandMenuInputProps. Source: src/unstable/command-menu/command-menu.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
className | string | – | |
placeholder | string | – |
CommandMenu.List
Client component Renders data-slot="command-menu-list" with the class nz-command-menu-list. Props: CommandMenuListProps. Source: src/unstable/command-menu/command-menu.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
childrenrequired | ReactNode | – | |
className | string | – | |
empty | ReactNode | – | Replaces |
CommandMenu.Group
Client component Renders data-slot="command-menu-group" with the class nz-command-menu-group. Props: CommandMenuGroupProps. Source: src/unstable/command-menu/command-menu.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
childrenrequired | ReactNode | – | |
className | string | – | |
headingrequired | ReactNode | – | The group's heading, in Select's group label look. |
CommandMenu.Item
Client component Renders data-slot="command-menu-item" with the class nz-command-menu-item. Props: CommandMenuItemProps. Source: src/unstable/command-menu/command-menu.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
childrenrequired | ReactNode | – | |
className | string | – | |
disabled | boolean | – | |
href | string | – | A link: choosing the row follows it. |
icon | ReactNode | – | A 20 px mark before the text. Decorative. |
kind | ReactNode | – | What it is ("Product", "Order", "Action"), at the row's end. |
onAction | () => void | – | Runs when the row is chosen (client components only). |
shortcut | string | – | Its own shortcut, drawn as a Kbd ( |
textValue | string | – | The text the search matches; defaults to |
CommandMenu.Footer
The keys the menu takes, as Figma's footer shows them. Decorative: the roles say the same.
Client component Renders no element of its own. Props: CommandMenuFooterProps. Source: src/unstable/command-menu/command-menu.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
className | string | – |
Other exports
CommandMenuFiltertypeimport type { CommandMenuFilter } from '@nizivo/react/unstable/command-menu'How the typed text matches an item's text: anywhere in it, or at its start; case and accents ignored.
type CommandMenuFilter = "contains" | "startsWith"CommandMenuFooterPropstypeimport type { CommandMenuFooterProps } from '@nizivo/react/unstable/command-menu'The props of
CommandMenu.Footer, listed above.CommandMenuGroupPropstypeimport type { CommandMenuGroupProps } from '@nizivo/react/unstable/command-menu'The props of
CommandMenu.Group, listed above.CommandMenuInputPropstypeimport type { CommandMenuInputProps } from '@nizivo/react/unstable/command-menu'The props of
CommandMenu.Input, listed above.CommandMenuItemPropstypeimport type { CommandMenuItemProps } from '@nizivo/react/unstable/command-menu'The props of
CommandMenu.Item, listed above.CommandMenuItemStatetypeimport type { CommandMenuItemState } from '@nizivo/react/unstable/command-menu'Name Type Default Description disabledrequiredboolean– focusVisiblerequiredboolean– highlightedrequiredboolean– The active row: the arrow keys or the pointer are on it.
CommandMenuLabelstypeimport type { CommandMenuLabels } from '@nizivo/react/unstable/command-menu'CommandMenu's own strings (§16.4): serialisable, English defaults.
Name Type Default Description emptyrequiredstring'No results'Shown when nothing matches.
labelrequiredstring'Command menu'The dialog's and the search field's name.
navigaterequiredstring'Navigate'The footer's arrow keys hint.
openrequiredstring'Open'The footer's Enter hint.
placeholderrequiredstring'Search'The search field's placeholder.
CommandMenuListPropstypeimport type { CommandMenuListProps } from '@nizivo/react/unstable/command-menu'The props of
CommandMenu.List, listed above.CommandMenuPopupPropstypeimport type { CommandMenuPopupProps } from '@nizivo/react/unstable/command-menu'The props of
CommandMenu.Popup, listed above.CommandMenuRootPropstypeimport type { CommandMenuRootProps } from '@nizivo/react/unstable/command-menu'The props of
CommandMenu.Root, listed above.CommandMenuTriggerPropstypeimport type { CommandMenuTriggerProps } from '@nizivo/react/unstable/command-menu'The props of
CommandMenu.Trigger, listed above.
Data attributes and slots
Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.
| Slot | Rendered by | Data attributes |
|---|---|---|
command-menu-group.nz-command-menu-group | CommandMenu.Grouproot (no render) | none |
command-menu-group-label.nz-command-menu-group-label | CommandMenu.Groupinner element (CSS only) | none |
command-menu-input.nz-command-menu-input | CommandMenu.Inputroot (no render); interactive, focus: self | none |
command-menu-item.nz-command-menu-item | CommandMenu.Itemroot (no render); interactive, focus: engine |
|
command-menu-list.nz-command-menu-list | CommandMenu.Listroot (no render) | none |
command-menu-trigger.nz-command-menu-trigger | CommandMenu.Triggerinner element (CSS only); interactive, focus: self | none |
CSS hooks
Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.
| Hook | Default | Read as |
|---|---|---|
--nz-command-menu-bg | --nz-bg-surface |
|
--nz-command-menu-item-bg-active | --nz-bg-hover |
|
Layers: nz.components. Classes: .nz-command-menu-empty, .nz-command-menu-field, .nz-command-menu-footer, .nz-command-menu-group, .nz-command-menu-group-label, .nz-command-menu-hint, .nz-command-menu-icon, .nz-command-menu-input, .nz-command-menu-item, .nz-command-menu-kind, .nz-command-menu-label, .nz-command-menu-list, .nz-command-menu-popup, .nz-command-menu-search, .nz-dialog-popup.
Tokens this stylesheet reads (29)
--nz-bg-hover, --nz-bg-subtle, --nz-bg-surface, --nz-border-subtle, --nz-border-width-default, --nz-font-body-l, --nz-font-body-m, --nz-font-label-m, --nz-font-label-s, --nz-icon-secondary, --nz-radius-sm, --nz-radius-xl, --nz-size-icon-md, --nz-space-0-5, --nz-space-1, --nz-space-1-5, --nz-space-2, --nz-space-3, --nz-space-4, --nz-space-5, --nz-space-6, --nz-text-disabled, --nz-text-primary, --nz-text-secondary, --nz-text-tertiary, --nz-tracking-body-l, --nz-tracking-body-m, --nz-tracking-label-m, --nz-tracking-label-s
Accessibility
These parts are interactive. Each gets the focus outline and touch-action rule from @nizivo/react/css/base.css (the nz.base layer), so the floor survives a restyle:
command-menu-input: draws the focus outline itself on keyboard focus.command-menu-item: draws the focus outline when the engine reports keyboard focus, not on pointer hover.command-menu-trigger: draws the focus outline itself on keyboard focus.
Keyboard
| Keys | Action |
|---|---|
| ⌘K, Ctrl+K | Opens the menu, also from a text field; again, closes it. |
| Typing | Filters the rows, case and accents ignored. |
| Arrow Down, Arrow Up | Move the active row; focus stays in the search field. |
| Enter | Runs the active row and closes the menu. |
| Escape | Clears the search first; on an empty search, closes the menu and returns focus. |
Source: React Aria Components 1.22.1 Autocomplete, SearchField and Menu (_engines/react-aria/command-menu.client.tsx) inside the Dialog; the shortcut from @nizivo/react/hotkeys. The browser lane (test/browser/command-menu.spec.tsx) checks ⌘K, the trigger, filtering, the arrow keys, Enter, Escape and the hotkeys policy.
Figma
Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.
- Canonical: Figma node
755:889 - Ghost masters: none
Combobox
A text input that suggests items from a list as the reader types, such as a country in a checkout form or search suggestions. Input's box, with Select's popup and rows; React Aria's ComboBox inside.
Container
Centres page content: at most --nz-layout-content-max wide (358, 976, 1280 px by viewport), with the responsive page margin --nz-grid-margin (16, 24, 32 px) on both sides.