CommandPalette
<CommandPalette> is the app-wide ⌘K menu. Wrap the app in CommandPaletteProvider once; anywhere inside, call useRegisterCommand to add an entry. The palette supports groups, shortcut hints, and the global hotkey is suppressed inside editable elements by default.
When to use
- Linear-style ⌘K menu for power-user actions and navigation.
- Workspace switchers, “create” shortcuts, in-app search of named objects.
Don’t use CommandPalette for: per-field option pickers (Combobox), navigation between top-level pages exclusively (a sidebar is better discovered), or actions that require parameters (open a Dialog on activation).
Basic
Press ⌘K (or Ctrl+K) to open the palette.
import { CommandPaletteProvider, useRegisterCommand,} from '@na/ui/components/CommandPalette';
// Wrap once at the app root<CommandPaletteProvider hotkey="mod+k" groupOrder={['Actions', 'Navigate']}> <App /></CommandPaletteProvider>
// Inside any componentfunction CreateDealAction() { const router = useRouter(); useRegisterCommand({ id: 'create-deal', label: 'Create deal', icon: <IconPlus />, shortcut: '⌘D', group: 'Actions', run: () => router.push('/deals/new'), }); return null;}API
CommandPaletteProvider
| Prop | Type | Default | Description |
|---|---|---|---|
hotkey | string | "mod+k" | Hotkey to toggle. Use `mod` for Cmd-or-Ctrl. Combine with `+`: `"mod+shift+p"`. |
suppressInEditableContext | boolean | true | Ignore the hotkey when the user is typing in an input / textarea / contentEditable. |
title | string | "Command Palette" | sr-only dialog title. |
description | string | "Search for a command to run." | sr-only dialog description. |
groupOrder | string[] | — | Group display order. Groups not listed appear after, in registration order. |
defaultGroup | string | "Actions" | Group name when an entry doesn't specify one. |
emptyState | ReactNode | "No results." | Empty-state copy when no entries match. |
CommandEntry
| Prop | Type | Default | Description |
|---|---|---|---|
id * | string | — | Unique entry identifier. |
label * | ReactNode | — | Visible label. |
icon | ReactNode | — | Leading icon. |
shortcut | ReactNode | — | Right-aligned keyboard hint. Informational; not bound by this primitive. |
group | string | defaultGroup | Group name. |
keywords | string[] | — | Search-time keywords. Defaults to the label when string. |
run * | () => void | — | Fires when the entry is activated. |
hidden | boolean | false | Hide without unregistering. |
useCommandPalette() / useRegisterCommand(entry, deps?)
useCommandPalette() returns { register, unregister, open, setOpen, toggle }. useRegisterCommand(entry, deps) is a thin hook that registers on mount and cleans up on unmount.
Design guidelines
✓ Do
- Group entries by purpose. "Actions", "Navigate", "Help" are the canonical three.
- Pair every shortcut hint with a real binding elsewhere. Hints without bindings train users to mistrust shortcuts.
- Use keywords for entries whose label hides the verb ("Settings → Billing" should match "billing").
✗ Don't
- Register entries that require parameters. Open a Dialog after activation instead.
- Override the suppress-in-editable behavior — users will hit ⌘K mid-typing and lose context.
- Show 100+ entries. The palette is a power-user shortcut, not a sitemap.
Accessibility
- The palette opens a Radix
Dialog— focus trapped, Esc closes. - Inner
Commandprovides full ARIA listbox semantics + type-ahead. - The hotkey is suppressed inside editable elements by default (configurable).