Command
StableA fast, keyboard-first list of commands and results with fuzzy search — inline, or as a ⌘K palette on top of the page.
Anatomy
- 1InputFilters the list as people type. Focus stays here while arrow keys move the selection.
- 2Group headingLabels a set of related results. Headings stay put while their items filter.
- 3Selected itemThe item Enter will run. The selection follows the arrow keys and the pointer.
- 4ShortcutThe keys that run the item without opening the palette. Informational only.
Installation
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/command.jsonUsage
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command"<Command>
<CommandInput placeholder="Type a command or search…" />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions">
<CommandItem onSelect={() => openCalendar()}>Calendar</CommandItem>
<CommandItem onSelect={() => openEmoji()}>Search emoji</CommandItem>
</CommandGroup>
</CommandList>
</Command>Command is built on cmdk. It filters and ranks items as you type, keeps one item highlighted, and runs its onSelect on Enter or click. The list animates its height as results change, so the palette never jumps.
Examples
Inline
Render Command directly for embedded pickers and launchers. Groups keep results scannable; CommandShortcut teaches the faster path.
Command palette
CommandDialog places the command list in a Dialog anchored high on the screen, so results grow downward without moving the input. Bind it to ⌘ K (this example uses ⌘ J to avoid clashing with this site's own search, which is built from the same component) and add CommandFooter for keyboard hints.
const [open, setOpen] = React.useState(false)
React.useEffect(() => {
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === "k" && (event.metaKey || event.ctrlKey)) {
event.preventDefault()
setOpen((value) => !value)
}
}
document.addEventListener("keydown", onKeyDown)
return () => document.removeEventListener("keydown", onKeyDown)
}, [])
return (
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput placeholder="Search…" />
<CommandList>{/* groups and items */}</CommandList>
<CommandFooter />
</CommandDialog>
)Empty state
CommandEmpty renders only when nothing matches. Make it useful: say what was searched, and suggest a way forward.
Keywords and aliases
People search with their own words. Add keywords so dark mode finds Appearance and invoice finds Billing — keywords count toward the match without being displayed.
Multiple selection
For pickers that toggle several values, keep the list open on select and set data-checked on items — prfct renders a check mark in the trailing position.
Async results
When results come from a server, turn off the built-in filtering with shouldFilter={false}, control the search with value and onValueChange on CommandInput, and render the items you fetch.
<Command shouldFilter={false}>
<CommandInput value={query} onValueChange={setQuery} />
<CommandList>
{isLoading && <CommandEmpty>Searching…</CommandEmpty>}
{results.map((result) => (
<CommandItem key={result.id} value={result.id} onSelect={open}>
{result.title}
</CommandItem>
))}
</CommandList>
</Command>Guidelines
When to use
- As a global palette (⌘ K) for navigation and actions in apps with more than a few screens.
- As an embedded, searchable list for pickers that outgrow a Select: assignees, labels, emoji, files.
When not to use
- For choosing a single form value — a Combobox is the form control, with a visible value, validation and a label.
- For short, fixed lists of actions — a Dropdown Menu is faster to scan than to search.
- As the only way to reach a command. A palette is an accelerator; everything in it should also exist in the interface.
Writing items
Name items the way people search for them: nouns for destinations (Settings, Billing), verb phrases for actions (Create project…, Invite teammate…). Put an ellipsis on items that open further input. Group results by kind — Navigation, Actions, Recent — and keep the most likely results in the first group.
Ranking
Items are ranked within their group as you type — prefix and whole-word matches first, scattered letters last — but groups stay in the order you render them. Put the group people reach for most first. For a palette that searches many sections, such as documentation, render the results as a single group while there is a query, so the best match is always the highlighted one. Keep keywords to a few words: long descriptions let almost any query match.
Performance
cmdk comfortably handles a few thousand items. Beyond that, filter on the server (see Async results) or cap the rendered results — the list isn't virtualized.
Accessibility
Command implements the WAI-ARIA combobox pattern: the input is a combobox that controls a listbox, and the highlighted option is announced through aria-activedescendant while focus stays in the input — so people can keep typing and navigating at once.
| Key | Behavior |
|---|---|
↓↑ | Highlights the next or previous item. Wraps around when loop is set. |
HomeEnd | Highlights the first or last item. |
⌘↓⌘↑ | Jumps to the last or first item. |
Alt↓Alt↑ | Jumps to the first item of the next or previous group. |
CtrlNCtrlJ | Highlights the next item (disable with vimBindings={false}). |
CtrlPCtrlK | Highlights the previous item. |
Enter | Runs the highlighted item's onSelect. |
Esc | In CommandDialog, closes the palette and returns focus to what opened it. |
- Label the palette.
CommandDialogrenders a visually hidden title and description — pass meaningfultitleanddescriptionprops. - Disabled items are skipped by navigation and announced as unavailable.
- Icons inside items are decorative; the item's text is what's announced and matched.
API reference
Command
The root, from cmdk.
CommandDialog
A Command inside a Dialog. Put CommandInput, CommandList and CommandFooter directly inside — the dialog provides the Command root (if you pass a Command of your own, it is used as the root instead). Accepts the Dialog root props (open, onOpenChange, …) plus:
CommandInput
CommandItem
CommandGroup
CommandList, CommandEmpty, CommandSeparator, CommandShortcut, CommandFooter
CommandList is the scrollable results region; it caps at min(24rem, 60dvh) and animates its height. CommandEmpty renders when there are no results. CommandSeparator accepts alwaysRender to stay visible during a search. CommandShortcut is a presentational trailing hint. CommandFooter renders navigation hints by default — pass children to replace them.