ComponentsOverlays & feedback

Command (⌘K)

Keyboard navigation, actions and record search from anywhere. Three layers: list primitives, a top-anchored CommandDialog, and a ready-made CommandPalette wired to a registry that pages, apps and the App shell's nav all contribute to.

cmdk, Radix Dialog components/command.tsx, styles/navigation.csstheme · material · accent · density · font

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { CommandProvider, CommandPalette, useRegisterCommands } from "@vamos-ui/react";

// App shell mounts these for you.
<CommandProvider actions={[{ id: "cms.post.new", title: "New post", shortcut: "mod+shift+n", global: true, perform: newPost }]}>
  <App />
  <CommandPalette />
</CommandProvider>

// Inside a page: commands that exist only while it is mounted.
useRegisterCommands([{ id: "cms.post.publish", title: "Publish post", group: "This post", perform: publish }]);

The kit only binds mod+k (palette) and mod+\ (sidebar rail). It never binds mod+b, mod+i or other editor shortcuts.

With a CommandSource active the palette ranks local commands itself (shouldFilter={false}), so local and remote results never fight over order.

Examples

Command dialog

CommandDialog is anchored near the top and fills the screen on phones. Focus starts in the input; Esc closes and returns focus.

Page commands

useRegisterCommands adds commands while the page is mounted and always calls the latest closure. A perform that returns a promise keeps the palette open with a spinner on the item.

“Local SEO checklist for 2026” · Draft

Record search

A CommandSource is debounced, aborted on every keystroke and shown in its own group with a thin progress bar while it loads. A failing source shows one line while other groups keep working.

Sub-pages

An action with children opens a sub-page (a chip in the input); Backspace on an empty input goes back. Children can load lazily. open("id") jumps straight to a sub-page.

API

Command

PropTypeDefault
filter
Score 0–1. The default folds text for the locale: exact > prefix > word start > contains > keyword.
(value, search, keywords) => numbercmdkFilter(locale)
shouldFilter
false: render only the items you want (async results).
booleantrue
loop
Arrow keys wrap around.
booleantrue
label
Accessible name of the listbox (command.label).
string"Command palette"
value / onValueChange
Controlled highlighted item.
string—

CommandInput

PropTypeDefault
placeholder
From the dictionary (command.placeholder).
string"Type a command or search…"
chip
Breadcrumb chip before the input (current sub-page).
ReactNode—
end
Trailing slot (spinner, filter).
ReactNode—
value / onValueChange
Controlled query.
string—

CommandItem

PropTypeDefault
value
Stable unique value; inferred from text when omitted.
string—
keywords
Synonyms used for matching.
string[]—
onSelect
Click or Enter.
(value: string) => void—
icon
Leading icon.
ReactNode—
description
Second line.
ReactNode—
shortcut
Display only: "mod+shift+n".
string | string[]—
badge
Trailing meta, e.g. an app badge.
ReactNode—
chevron
Trailing chevron (opens a sub-page).
boolean—
loading
Spinner in place of the icon, aria-busy.
boolean—
disabled
Skipped by the keyboard.
boolean—

CommandGroup · CommandEmpty · CommandLoading · CommandSeparator · CommandShortcut

PropTypeDefault
heading
CommandGroup title (also its accessible name).
ReactNode—
children
CommandEmpty / CommandLoading content; defaults from the dictionary.
ReactNode"No results found."
keys
CommandShortcut keys, platform-aware (⌘ / Ctrl).
string | string[]—

CommandDialog

PropTypeDefault
open / defaultOpen / onOpenChange
Dialog state.
boolean—
size
560 / 680 px. Fullscreen below 640px.
"md" | "lg""lg"
title
Hidden dialog title.
string"Command palette"
commandProps
Props for the inner Command (filter, shouldFilter…).
CommandProps—

CommandProvider

PropTypeDefault
actions
Static commands for the provider's lifetime.
CommandAction[]—
sources
Async record search.
CommandSource[]—
registry
Use an existing registry (createCommandRegistry).
CommandRegistry—
shortcut
Toggle shortcut. Inside inputs only shortcuts with mod fire.
string | false"mod+k"
open / defaultOpen / onOpenChange
Palette state.
boolean—

CommandPalette

PropTypeDefault
size
Dialog width.
"md" | "lg""lg"
placeholder
Input placeholder.
string—
recents
Recently used commands first (localStorage ui-cmd-recent, 8 ids).
booleantrue
footer
Hint bar: ↑↓ navigate · ↵ select · ⌫ back · esc close.
booleantrue
groupOrder
Group order for the empty query.
string[]—
labels
Override built-in strings.
Partial<CommandLabels>—

CommandAction

PropTypeDefault
id
Module-prefixed and unique: "cms.post.new".
string—
title / subtitle
Row text; both are searched.
string—
group
"Go to", "Posts"…
string"Actions"
icon
Leading icon.
LucideIcon | ReactNode—
keywords
Synonyms.
string[]—
shortcut
With global: true it works while the palette is closed.
string | string[]—
href
Navigates through the app router.
string—
perform
Runs the action; a promise keeps the palette open until it settles.
(ctx) => void | Promise—
children
Opens a sub-page.
CommandAction[] | () => Promise<CommandAction[]>—
permission
Hidden unless can(permission).
string | string[]—
when
Contextual visibility.
(ctx) => boolean—
priority
Higher first among equal matches.
number—
app
App badge in multi-app shells.
string—

Hooks

PropTypeDefault
useRegisterCommands(actions, deps?)
Adds commands while mounted; ref-counted and StrictMode-safe.
void—
useCommandSource(source)
Adds an async source while mounted.
void—
useCommandPalette()
Control the palette from anywhere.
{ open(pageId?), close, toggle, isOpen, available }—
createCommandRegistry({ actions, sources })
A store (useSyncExternalStore) to share between providers.
CommandRegistry—