Command Palette
A searchable command palette with groups, nested pages, and recent actions.
Nothing run yet.
Installation
Usage
Describe your commands as data and get the whole ⌘K surface: fuzzy search, groups, keyboard navigation, a nested page per action that has children, and a recents list that survives reloads.
import { CommandPalette } from "@/components/ui/command-palette";
const ACTIONS = [
{
id: "new-project",
label: "New project",
group: "Create",
shortcut: "⌘ N",
onSelect: () => router.navigate({ to: "/projects/new" }),
},
{
id: "theme",
label: "Change theme",
group: "Preferences",
children: [
{ id: "theme-light", label: "Light", onSelect: () => setTheme("light") },
{ id: "theme-dark", label: "Dark", onSelect: () => setTheme("dark") },
],
},
];
export function AppCommands() {
return <CommandPalette actions={ACTIONS} />;
}Mount it once, near the root. It binds mod+k itself, so nothing else needs wiring.
Nested pages
An action with children opens them as a page instead of running. The breadcrumb shows where you
are, Backspace on an empty query goes back, and search applies to the current page only — which is
what makes a two-level menu (Change theme → Dark) feel faster than a flat list of every permutation.
Recents
Selected actions are remembered in localStorage and shown above everything else when the query is
empty. They are resolved by id against the current action list, so a command you removed from the
app simply stops appearing.
<CommandPalette actions={ACTIONS} recentsKey="acme:commands" maxRecents={3} />Pass recentsKey={null} to turn the feature off — worth doing if the palette can act on data the
user may no longer be allowed to see.
Controlled
Pass open and onOpenChange to drive it from elsewhere — a toolbar button, a route, an onboarding
step. Set hotkey={null} if you want to own the shortcut.
<CommandPalette actions={ACTIONS} open={open} onOpenChange={setOpen} hotkey={null} />Action shape
| Field | Description |
|---|---|
id | Stable identity. Used for recents, so keep it stable across releases. |
label | What the user reads and searches. |
description | Second line, also searchable. |
group | Heading to file the action under. Ungrouped actions render first. |
keywords | Extra search terms — synonyms, the old name of a feature, a misspelling. |
shortcut | Display only. Render the shortcut you bound elsewhere; the palette does not bind it. |
icon | Any node. |
children | Opens a nested page instead of running. |
onSelect | What the action does. Called after the palette closes, so a focus change lands cleanly. |
Props
| Prop | Description |
|---|---|
actions | The root action list. |
open / onOpenChange | Control the palette externally. |
hotkey | Toggle shortcut. Defaults to "mod+k" — ⌘ on Apple platforms, Ctrl elsewhere. null disables it. |
placeholder | Input placeholder on the root page. |
emptyMessage | Shown when nothing matches. |
recentsKey | localStorage key for recents. null disables them. |
maxRecents | How many to keep. Defaults to 5. |