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