Skip to content
Back to Design Hub

Command Palette (Cmd+K)

A keyboard-first search over all actions and pages — the power-user shortcut to everything.

UX PatternsAdvanced

Why

Power users hate leaving the keyboard to click through menus. A command palette (Cmd+K) lets them jump to any page, run any action, or insert any template by typing — Linear/Notion/Raycast's signature feature.

Nielsen Norman Group research shows users spend an average of 10-15 seconds on a page before leaving. Clear visual hierarchy and progressive disclosure improve task completion rates by 40-60%.

When

Any productivity app with >20 actions or pages: editors, dashboards, admin tools. Skip for simple marketing sites or single-purpose apps where nav is already obvious.

How

1. Trigger — Cmd/Ctrl+K opens; Esc closes; global listener, not per-page 2. Input — single text field, fuzzy-search over all commands 3. Commands — { id, title, shortcut?, run(), group } — pages, actions, settings, recent 4. Fuzzy search — fuse.js or uFuzzy; rank by recency + relevance; show top 8 5. Keyboard — ↑↓ navigate, Enter run, Esc close, Tab accept argument for parameterized commands 6. Grouping — Pages, Actions, Settings, Recent — labeled sections in the list 7. Hints — show the shortcut next to each command ("Jump to Inbox ⌘1") for discoverability 8. Recency — boost recently-used commands to the top; users re-run the same 5 actions

Example

import { Command } from "cmdk"; // the library Vercel/Linear use function CommandPalette() { const [open, setOpen] = useState(false); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === "k" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); setOpen((o) => !o); } }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, []); return ( <Command.Dialog open={open} onOpenChange={setOpen}> <Command.Input placeholder="Type a command or search…" /> <Command.List> <Command.Empty>No results.</Command.Empty> <Command.Group heading="Pages"> <Command.Item onSelect={() => go("/inbox")}>Go to Inbox <kbd>⌘1</kbd></Command.Item> <Command.Item onSelect={() => go("/settings")}>Go to Settings</Command.Item> </Command.Group> <Command.Group heading="Actions"> <Command.Item onSelect={newDoc}>New Document <kbd>⌘N</kbd></Command.Item> <Command.Item onSelect={toggleTheme}>Toggle Theme</Command.Item> </Command.Group> </Command.List> </Command.Dialog> ); }

Common Problems

  • ⚠No global Cmd+K — palette only opens from a button, power users can't trigger from anywhere
  • ⚠Static ranking — same 50 commands in the same order, recency would surface the 5 they actually use
  • ⚠No shortcut hints — users don't discover the ⌘1 shortcuts because they're hidden in settings

Best Practices

  • ✓Global Cmd+K listener — opens from anywhere, not just when a button is focused
  • ✓Rank by recency + relevance — the 5 commands a user runs daily should be at the top
  • ✓Show shortcut hints inline — 'New Document ⌘N' teaches the shortcut while using the palette

FAQ