Command

Finds and runs any action by typing part of its name.

Examples

Actions, agents and settings

↑↓Move↵Run

Opened with a shortcut

Filtering

  • built indefault

  • by your server

States

  • rest

  • searching

  • keyword match

  • no match

  • loading

Usage

When to use it
Use
An app has more actions and items than its navigation shows: Cmd K for everything.
Use
Picking one of many named things where typing is faster than scrolling: an agent, a workspace.
Not for
Three to twelve actions on one item. Use Dropdown menu.
Not for
Choosing a value for a form field. Use Combobox.
Not for
Filtering a table that stays on screen. Use Filter bar.

Rules

9 rules
Should
Pass keywords with the other names people use, such as ['ship', 'release'] for Publish prompt release.
Must
Set shouldFilter={false} and mount CommandLoading while a server request runs. Because the client filter hides server results whose text lacks the query, and without CommandLoading the list says No results while the request runs.
Must
Wrap item text in CommandLabel when the item holds an icon or shortcut. Because only a string child gets its matched letters drawn, so a mixed item shows no reason it matched.
Should
Start an action with its verb and name things by their own name; ranking favours the first word.
Should
Say what can be found in the placeholder, such as "Search commands and agents".
Must
Give CommandDialog a specific title. Because the sr-only title names the dialog, and the default "Command palette" does not say what it searches.
Never
Swap in a loose fuzzy matcher. Because scattered-letter matches rank unrelated items first, and Enter runs the first item.
Should
Give every item in a group an icon or none, only from the allowed action set (see Iconography), and none to named things such as agents.
Should
Keep the list at 320px and filter on the server past 500 items.

Accessibility

6 checks
The input is a combobox with aria-controls, aria-activedescendant and aria-autocomplete="list".
The field and the list are named by label; the list is a listbox holding only groups and options, with aria-selected, aria-posinset and aria-setsize in screen order.
Up and Down (Ctrl P, Ctrl N) move the active item; Home and End move it when the field is empty.
Enter runs the active item, which after typing is the best match.
CommandDialog traps focus, makes the page inert and closes with Escape back to the opener.
A polite status outside the list announces the result count, including "1 result, unavailable" for a disabled match, and CommandLoading's words while it is mounted.

Edge cases

12 edge cases
72-character ID, 90-character label
Truncates with an ellipsis on one line; the shortcut keeps its place.
One-letter search
Names starting with it rank first across groups, then word starts, then the rest.
No items
Empty shows "No results" top-left, in Empty's plain look.
500 items
Scrolls inside 320px (--popup-max) with an edge fade; arrows keep the active item in view.
Right to left
The icon and search glyph lead on the right.
Request never returns
The spinner stays; Empty never shows; typing continues.
Request fails
Render a CommandEmpty with the error text and a retry item.
Typing fast
Re-ranks every key; the active item resets to the best match.
Inside a Popover
Escape closes the container.
Touch
Items grow to 44px; the input uses 16px text so iOS does not zoom.
Forced colors
The active item takes Highlight; disabled takes GrayText; matched letters keep their underline.
Reduced motion
The spinner stops and shows the static glyph.

API

9 props
Command label
string default 'Commands'
Command shouldFilter
boolean default true
Command filter
(value, search, keywords) => number default ranked word match
Command loop
boolean default false
CommandInput value, onValueChange
string
CommandItem value, keywords
string, string[] default text content
CommandItem onSelect
(value) => void
CommandItem disabled, forceMount
boolean default false
CommandDialog title, description
ReactNode | null default 'Command palette'

Tokens

19 tokens
--surface-page
var(--paper)
--radius-inner
4px
--radius-soft
6px
--radius-frame
10px
--line-control
oklch(var(--ink-c) / 0.16)
--border-focus
var(--ink)
--line-row
oklch(var(--ink-c) / 0.08)
--surface-hover
oklch(var(--ink-c) / 0.05)
--text-disabled
oklch(var(--ink-c) / 0.40)
--type-label
400 14px/16px var(--font-sans)
--text-2
oklch(var(--ink-c) / 0.80)
--text-1
var(--ink)
--space-6
24px
--line-frame
oklch(var(--ink-c) / var(--line-frame-alpha))
--type-body-sm
380 13px/18px var(--font-sans)
--text-3
oklch(var(--ink-c) / 0.66)
--dur-spin
800ms
--ease-spin
linear
--popup-max
320px

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.