apx-dsv0.1Local renderer · live
K
Foundations4
  • Getting started
  • Theming
  • Templates
  • Icons
  • Avatar
  • Badge
  • DataGrid
  • Scheduler
  • Stat
  • Table
  • Timeline
  • TreeView
  • Accordion
  • Alert
  • EmptyState
  • Progress
  • Skeleton
  • Spinner
  • SplashScreen
  • Toast
  • ColorPicker
  • FileUpload
  • Form
  • Rating
  • TagsInput
  • Combobox
  • Field
  • Select
  • Toggle
  • Button
  • Calendar
  • Checkbox
  • DatePicker
  • Input
  • NumberInput
  • Radio
  • Slider
  • Switch
  • Textarea
  • AppShell
  • Div
  • Divider
  • Sidebar
  • Stack
  • Typography
  • Image
  • Breadcrumbs
  • Carousel
  • NavigationMenu
  • Pagination
  • Stepper
  • Tabs
  • Toolbar
  • CommandPalette
  • Confirm
  • Drawer
  • HoverCard
  • Menu
  • Modal
  • Popover
  • Tooltip
  • Icon
  • Card
  • PricingCard
60 componentsapx-ds/renderer
CommandPalette
Variant↳ other

Overlays

CommandPalette

The ⌘K command launcher — a keyboard-first Modal overlay with a search input, filterable result list, sub-pages, recent-command surfacing, and a global hotkey to toggle visibility. Three registration paths (declarative

Overview

<CommandPalette /> is the canonical ⌘K command launcher — a Modal-styled overlay with a search input, a filterable result list, optional sub-pages, and a global hotkey to toggle visibility. It composes existing primitives rather than introducing new architecture:

  • Dialog shell — <Modal>. Backdrop, focus trap, scroll lock, escape stack, portal.
  • Filter logic — filterStrategies + fuzzyMatch from Combobox. Substring / startsWith / fuzzy / custom.
  • Keyboard navigation — _shared/useListKeyboard for arrow-key highlight and Enter-to-select.
  • Module-level imperative API — commands.register() and palette.open() work from any JS context.
  • Inline keyboard glyphs — <Kbd> primitive, exported separately for shortcut hints anywhere in the app.

Overview — search, categories, and keyboard shortcuts

Loading preview…
Overview.tsx

When to use

  • A launcher for app-wide actions ("New document", "Switch workspace", "Toggle theme") — ⌘K is the canonical gesture.
  • A navigator across hundreds of objects (issues, files, members) — search-narrowed nav is faster than menu trees.
  • A shortcut help layer — even if it never opens, the registered commands document every action with their hotkeys.

When NOT to use

  • A picker bound to a single field value → <Combobox />.
  • A list of contextual actions on a specific row / element → <Menu />.
  • A single confirmation step → <Modal> directly.

Anatomy

text
<CommandPalette
  open onOpenChange defaultOpen
  commands=[…]                         // declarative list
  pages={ <pageId>: { title, placeholder, commands: […] } }
  recentCommandIds=[…] maxRecentItems
  hotkey="$mod+K"  disableGlobalHotkey
  filterStrategy="substring|fuzzy|startsWith|custom"
  filterCommand=(cmd, query) => boolean
  matchKeywords
  renderCommand renderEmpty renderFooter
  variant="solid|soft|minimal"
  size="sm|md|lg"
  color="primary|secondary|success|warning|danger|info|neutral"
  placeholder ariaLabel
  translations={…}                     // merged over DEFAULT_COMMAND_PALETTE_TRANSLATIONS
  trackRecents
  portalContainer
  className style sx
/>

// Three registration APIs (all interop via the same store)
commands.register({ id, label, onSelect, … })            // imperative
commands.unregister(id)
commands.registerMany([…])                               // batched, single emit
useRegisterCommand({ id, label, onSelect, … })           // hook
<CommandPalette commands={[…]} />                        // declarative prop

// Module-level palette controller
palette.open()
palette.close()
palette.toggle()
palette.openPage(pageId)
palette.popPage()
palette.setQuery(query)

// Kbd primitive (separate export, useful outside the palette)
<Kbd>K</Kbd>
<Kbd keys={['Ctrl', 'Shift', 'P']} separator="+" />
<Kbd platform="mac">cmd</Kbd>                            // → ⌘

// Public hotkey utilities
parseHotkey('$mod+K')                                    // → { mod, key, … }
matchesHotkey(event, parsed)                             // → boolean
useGlobalHotkey({ hotkey, onTrigger })                   // window listener
<CommandPalette
  open onOpenChange defaultOpen
  commands=[…]                         // declarative list
  pages={ <pageId>: { title, placeholder, commands: […] } }
  recentCommandIds=[…] maxRecentItems
  hotkey="$mod+K"  disableGlobalHotkey
  filterStrategy="substring|fuzzy|startsWith|custom"
  filterCommand=(cmd, query) => boolean
  matchKeywords
  renderCommand renderEmpty renderFooter
  variant="solid|soft|minimal"
  size="sm|md|lg"
  color="primary|secondary|success|warning|danger|info|neutral"
  placeholder ariaLabel
  translations={…}                     // merged over DEFAULT_COMMAND_PALETTE_TRANSLATIONS
  trackRecents
  portalContainer
  className style sx
/>

// Three registration APIs (all interop via the same store)
commands.register({ id, label, onSelect, … })            // imperative
commands.unregister(id)
commands.registerMany([…])                               // batched, single emit
useRegisterCommand({ id, label, onSelect, … })           // hook
<CommandPalette commands={[…]} />                        // declarative prop

// Module-level palette controller
palette.open()
palette.close()
palette.toggle()
palette.openPage(pageId)
palette.popPage()
palette.setQuery(query)

// Kbd primitive (separate export, useful outside the palette)
<Kbd>K</Kbd>
<Kbd keys={['Ctrl', 'Shift', 'P']} separator="+" />
<Kbd platform="mac">cmd</Kbd>                            // → ⌘

// Public hotkey utilities
parseHotkey('$mod+K')                                    // → { mod, key, … }
matchesHotkey(event, parsed)                             // → boolean
useGlobalHotkey({ hotkey, onTrigger })                   // window listener

Three registration APIs

Each team has a different pattern; one API doesn't fit all. The store deduplicates by id so the three paths interop cleanly — declarative wins on collision because it's the most explicit signal.

APIWhen
commands propTop-level, static list. Doc / settings / onboarding flows.
useRegisterCommand()Commands defined deep in the tree, lifecycle-bound. Editor toolbars.
commands.register()Outside React. Action creators, error handlers, service workers.

Sub-pages

A command can push a sub-palette via pushPage(pageId) from its onSelect. The pages prop maps page ids to their command lists + title + placeholder. Esc pops, Backspace-on-empty-query pops. No re-mount of the dialog — focus stays trapped inside the same Modal.

Recents

When the query is empty, the most-recent N selected commands surface in a dedicated "Recently used" section above the categories. Defaults to in-memory tracking; pass recentCommandIds from your own store (Redux, Zustand, localStorage, …) to persist across sessions. Pass trackRecents={false} to disable.

Hotkey

Defaults to '$mod+K' — Cmd on macOS, Ctrl elsewhere. Pass a fully-qualified string ('Ctrl+P', 'Cmd+/') to lock to a single platform. Set disableGlobalHotkey when the host app already binds the shortcut and toggles via palette.toggle().

The hotkey utilities — parseHotkey, matchesHotkey, useGlobalHotkey — are exported publicly so consumers can build their own shortcut help / binding UIs without duplicating the parser.

Accessibility

Full W3C Combobox in a Modal pattern:

  • Modal: role="dialog" + aria-modal="true" + aria-label.
  • Input: role="combobox" + aria-expanded="true" + aria-controls={listId} + aria-autocomplete="list" + aria-activedescendant={highlightedId}.
  • List: role="listbox".
  • Rows: role="option" + aria-selected + tabIndex={-1} (so the input retains focus while the highlight moves).
  • Category headers: role="presentation" (decorative).
  • Footer hints: aria-hidden="true".

Focus management:

  • Opening focuses the input (via Modal's initialFocus).
  • Closing returns focus to the trigger (Modal default).
  • Esc closes at root; Esc inside a sub-page pops back (we stopImmediatePropagation so Modal's escape stack stays quiet).
  • Tab is trapped inside the Modal.

Live announcements: result count is announced via aria-live="polite" whenever the query changes.

Axe: 0 violations across the variant × size × color matrix, including the sub-page and empty states.

I18n

Inline translations prop merges over DEFAULT_COMMAND_PALETTE_TRANSLATIONS. No <I18nProvider> integration yet (the provider hasn't shipped); the prop is the temporary surface and will dovetail with the provider when it lands.

More examples

AsyncCommand

Loading preview…
AsyncCommand.tsx

Basic

Loading preview…
Basic.tsx

Colors

Loading preview…
Colors.tsx

CustomHotkey

Loading preview…
CustomHotkey.tsx

CustomRenderRow

Loading preview…
CustomRenderRow.tsx

FuzzyFilter

Loading preview…
FuzzyFilter.tsx

HookRegistration

Loading preview…
HookRegistration.tsx

ImperativeRegistration

Loading preview…
ImperativeRegistration.tsx

KbdShowcase

Loading preview…
KbdShowcase.tsx

RecentCommands

Loading preview…
RecentCommands.tsx

Sizes

Loading preview…
Sizes.tsx

SubPages

Loading preview…
SubPages.tsx

Variants

Loading preview…
Variants.tsx

WithCategories

Loading preview…
WithCategories.tsx

Props

PropTypeDefaultDescription
ariaLabelstring—Accessible label for the dialog. Falls back to `translations.paletteLabel`. Use when the palette serves a specialized purpose ("Theme picker", "Quick switcher", …) so the AT user hears the right context.
classNamestring—Class applied to the dialog Content surface.
colorenum——
commandsCommand[]—Declarative command list. Merged with any commands registered via `useRegisterCommand` / `commands.register()` (declarative wins on id collision — the prop is the "ground truth" for the lifetime of this palette mount).
defaultOpenboolean——
disableGlobalHotkeyboolean—Disable the global hotkey listener (when the host app handles it). Default: `false`.
filterCommand(cmd: Command, query: string) => boolean——
filterStrategyenum——
hotkeystring—Hotkey to toggle the palette. Default: `'$mod+K'`.
matchKeywordsboolean—Also match against the command's `keywords` array. Default: `true`.
maxRecentItemsnumber—Max recent items displayed. Default: `5`.
onOpenChange(open: boolean) => void——
openboolean——
pagesRecord<string, CommandPalettePage>—Sub-palette pages keyed by id.
placeholderstring——
portalContainerHTMLElement | null—Portal target. Defaults to `document.body`.
recentCommandIdsstring[]—Ordered list of recently used command ids (most recent first). Shown above the filtered list when the query is empty. The component tracks recents in-memory automatically if this prop is omitted; pass it explicitly to control persistence yourself.
renderCommand(ctx: RenderCommandContext) => ReactNode—Override row rendering. Receives the full command + helpers.
renderEmpty(query: string) => ReactNode—Empty-state renderer. Receives the current query.
renderFooter((helpers: { close: () => void; t: CommandPaletteTranslations; }) => ReactNode) | null—Footer renderer. Defaults to the built-in hint strip (translated). Pass `null` to hide.
sizeenum——
styleCSSProperties—Inline style applied to the dialog Content surface.
sxSx—Theme-aware style overrides (merged after recipe + className).
trackRecentsboolean—Track recently used commands in-memory after each select. Default: `true`. Disable when you're passing `recentCommandIds` from external persistence.
translationsPartial<CommandPaletteTranslations>—Inline translation overrides. Merged over `DEFAULT_COMMAND_PALETTE_TRANSLATIONS`.
variantenum——