Skip to content

Command

Stable

A fast, keyboard-first list of commands and results with fuzzy search — inline, or as a ⌘K palette on top of the page.

No results found.

Anatomy

  1. 1InputFilters the list as people type. Focus stays here while arrow keys move the selection.
  2. 2Group headingLabels a set of related results. Headings stay put while their items filter.
  3. 3Selected itemThe item Enter will run. The selection follows the arrow keys and the pointer.
  4. 4ShortcutThe keys that run the item without opening the palette. Informational only.

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/command.json

Usage

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.

No results found.

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.

No repositories foundCheck the spelling, or search across all organizations with org:

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.

No settings match.

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.

No labels found.

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.

Actions
Create project… ⌘N
Invite teammate…
Do.Specific, searchable labels grouped by kind, with the shortcut that skips the palette next time.
New
People stuff
Misc
Don’t.Vague labels that match nothing people actually type.

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.

KeyBehavior
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.
AltAlt
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. CommandDialog renders a visually hidden title and description — pass meaningful title and description props.
  • 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.

PropTypeDefault
label

Accessible label for the command menu.

stringNo default
shouldFilter

Set to false to filter and sort items yourself, e.g. for server results.

booleantrue
filter

Custom ranking function. Return 0 to hide an item, 1 for a perfect match.

(value: string, search: string, keywords?: string[]) => numberNo default
value

Controlled value of the highlighted item.

stringNo default
defaultValue

Initially highlighted item when uncontrolled.

stringNo default
onValueChange

Called when the highlighted item changes.

(value: string) => voidNo default
loop

Whether arrow keys wrap from the last item to the first.

booleanfalse
vimBindings

Enables Ctrl+N/J/P/K navigation.

booleantrue

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:

PropTypeDefault
title

Visually hidden dialog title for assistive technology. Defaults to the locale's message.

stringmessages.commandPalette
description

Visually hidden dialog description. Defaults to the locale's message.

stringmessages.commandDescription
showCloseButton

Shows the dialog's close button.

booleanfalse
className

Classes for the dialog surface.

stringNo default
commandProps

Props for the inner Command root, such as filter, shouldFilter, loop or a controlled value.

ComponentProps<typeof Command>No default

CommandInput

PropTypeDefault
value

Controlled search string.

stringNo default
onValueChange

Called as the search changes.

(search: string) => voidNo default
placeholder

Hint shown when the input is empty.

stringNo default

CommandItem

PropTypeDefault
onSelect

Called on Enter or click.

(value: string) => voidNo default
value

Value used for filtering and selection. Defaults to the item's text content.

stringNo default
keywords

Extra terms that match the item without being displayed.

string[]No default
disabled

Skips the item in navigation and ignores selection.

booleanfalse
forceMount

Always render, regardless of the search.

booleanfalse
data-checked

Shows a trailing check mark for multi-select lists.

booleanNo default

CommandGroup

PropTypeDefault
heading

Heading shown above the group and used as its accessible name.

ReactNodeNo default
value

Required when there is no heading; must be unique.

stringNo default
forceMount

Always render the group, even when none of its items match.

booleanfalse

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.