Command
A search box over a list of actions, always open: type to narrow it, arrows to move, Enter to run. CommandDialog floats it over the dimmed page.
Install
# once per project: where @zeroui items come frompnpm dlx shadcn@latest registry add @zeroui=https://zeroui.catforest.dev/r/{name}.jsonpnpm dlx shadcn@latest add @zeroui/commandExamples
Inline, and as a ⌘K palette
Take the job⌘T
Hand in
Abandon
Job board
District map
Drivers
import { useEffect, useState } from "react";import { Button } from "@/components/zero/button";import { Command, CommandCollection, CommandDialog, CommandEmpty, CommandGroup, CommandGroupLabel, CommandInput, CommandItem, CommandList, CommandShortcut,} from "@/components/zero/command";type Action = { value: string; label: string; keys?: string };const groups: { value: string; items: Action[] }[] = [ { value: "Jobs", items: [ { value: "take", label: "Take the job", keys: "⌘T" }, { value: "hand-in", label: "Hand in" }, { value: "abandon", label: "Abandon" }, ], }, { value: "Go to", items: [ { value: "board", label: "Job board" }, { value: "map", label: "District map" }, { value: "drivers", label: "Drivers" }, ], },];function Palette({ onRun }: { onRun?: () => void }) { return ( <Command items={groups}> <CommandInput placeholder="Type a command" aria-label="Command" /> <CommandList> {(group: (typeof groups)[number]) => ( <CommandGroup key={group.value} items={group.items}> <CommandGroupLabel>{group.value}</CommandGroupLabel> <CommandCollection> {(item: Action) => ( <CommandItem key={item.value} value={item} onClick={onRun}> {item.label} {item.keys ? ( <CommandShortcut>{item.keys}</CommandShortcut> ) : null} </CommandItem> )} </CommandCollection> </CommandGroup> )} </CommandList> <CommandEmpty>Nothing matches.</CommandEmpty> </Command> );}export default function CommandPalette() { const [open, setOpen] = useState(false); useEffect(() => { const onKeyDown = (event: KeyboardEvent) => { if (event.key === "k" && (event.metaKey || event.ctrlKey)) { event.preventDefault(); setOpen((current) => !current); } }; window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }, []); return ( <div className="grid w-full max-w-md justify-items-start gap-4"> <Palette /> <Button variant="secondary" onClick={() => setOpen(true)}> Open the palette (⌘K) </Button> <CommandDialog open={open} onOpenChange={setOpen}> <Palette onRun={() => setOpen(false)} /> </CommandDialog> </div> );}Behaviour
The first match is always highlighted, so Enter runs it. The search sits on a black bar; the rows are the Dropdown menu's, filled with the accent while highlighted. Pass the items, or groups of items, to Command and render them from CommandList's function child; an item's label is what typing matches.
It is built on Base UI's Autocomplete, so it needs no other library. The ⌘K shortcut is yours to wire, as in the example.
Props
Command
| Prop | Type | Default | Description |
|---|---|---|---|
| items | Item[] | Group[] | — | What to list; each item needs a label. |
| filter | (item, query) => boolean | — | Replaces the built-in matching. |
Also takes every prop of Base UI's Autocomplete.Root.
CommandDialog
| Prop | Type | Default | Description |
|---|---|---|---|
| open / onOpenChange | boolean / (open) => void | — | Open, controlled. |
| title | string | "Command palette" | The dialog's accessible name. |
CommandInput, CommandList, CommandEmpty, CommandGroup, CommandGroupLabel, CommandCollection, CommandItem, CommandSeparator, CommandShortcut
Also takes every prop of Base UI's Autocomplete.Input.