Command menus 101: why every modern app has one
From Spotlight to Linear's K-bar. What makes a good command menu, and how to ship one without rebuilding it from scratch.
Hit ⌘K in almost any product shipped in the last three years and a small floating panel appears. Type a few letters, hit enter, and you've navigated, run a command, or invoked an action. Linear made it table stakes. Raycast made it a product. Now it's the default interaction model for any app dense enough to need one.
It's also a deceptively hard pattern. Most teams ship it, immediately regret the implementation, and rebuild it 18 months later. Here's what makes the good ones work.
Why command menus won
Three reasons. First, density. Once an app has more than ~25 features, a sidebar can't show them all and a settings menu buries them. The command menu is the flat index. Second, speed. Power users would rather type four letters than move a mouse, and power users determine whether your product feels fast. Third, discoverability. A good command menu is also a self-documenting list of every capability the product has, which beats a docs site for the 80% case.
The anatomy of a good one
The input
Auto-focused on open. Placeholder is a directive ("Type a command or search") not a label. No icon on the left larger than 16px, the input should feel like a terminal, not a search bar. ESC closes; ⌘K toggles.
The list
- Grouped by intent, not by alphabet. Navigate at the top, Create in the middle, Settings near the bottom. The groups themselves are small mono labels.
- Each row is one line, with an icon on the left, the action name, an optional dim subtext, and the keyboard shortcut right-aligned in mono.
- Selected row is high-contrast, not just a subtle background tint. The cursor needs to be unambiguous when keyboard-navigating fast.
- Results refresh on every keystroke with no debounce. Anything slower than ~30ms feels broken at this UI density.
The matching
Use fuzzy matching, not prefix matching. "lndr" should find "Linear". The two libraries to know are cmdk (the React command menu Vercel maintains) and fuse.js for the matching algorithm. Don't write your own fuzzy matcher unless you have a strong reason, it's a deeper problem than it looks.
Context-aware results
The best command menus show different commands depending on where you are in the app. On an invoice page, "Mark as paid" appears at the top. On the dashboard, it doesn't. This is what separates a true command menu from a glorified search box, the menu knows about your current scope.
The patterns to copy
Subpages and back-navigation
Pressing enter on a command sometimes runs the command, sometimes navigates into a subpanel ("Change theme" opens a list of themes). Backspace at an empty input returns to the parent. This nested navigation is what lets a command menu replace 80% of a settings UI.
Recents and pinned
First-open should show recent commands at the top, pinned commands above those. Empty state for a brand-new user should be the most useful 8–10 actions, not a blank slate. The empty state is a tutorial.
Inline previews
Raycast popularized previews, hover a result and a panel on the right shows what you'd see if you ran it. Borrow this when your commands have nontrivial output (search results, file contents, calendar events). Skip it when commands are atomic actions.
The mistakes
- 01Building the UI but not the keyboard model. A command menu you can only operate with arrow keys + enter is a command menu, adding mouse interaction is fine, but a mouse-first command menu is a search bar with bad UX.
- 02Showing too many results. If the menu has 200 commands, the user types two characters and sees 47 results. Hard-cap at ~8 visible, paginate the rest.
- 03Forgetting the close-on-execute behavior. Most commands should close the menu after executing. The few that shouldn't (running multiple actions in a row) need explicit affordance.
- 04Confusing it with global search. A command menu can include search results, but its primary job is actions. If 80% of your menu is search hits with no commands, you built a search bar.
Ship one
The interaction entry in the directory ships a tuned prompt that produces a Linear-style command menu, cmdk under the hood, fuzzy matching, grouped results, keyboard shortcuts on the right, ⌘K to toggle. Drop it in and customize the action list. Two hours of work for a feature your users will use a hundred times a week.
Keep reading
Navigation patterns that stay out of the way
Sticky bars, mega menus, sidebars, mobile sheets. The four navigation models, and the rules that make each one quiet.
Modals, sheets, and drawers: when to interrupt
Dialog, sheet, drawer, popover, four ways to overlay content, four different jobs. The decision tree, the spec, and the accessibility traps.