Command Palette
A framework-free ⌘K palette with nested pages, async sources and shortcuts.
npm install @devix-labs/command-palette
A command palette on a native <dialog>, so the focus trap, Escape and the top layer are the browser's. Ranking is documented and deterministic — match quality, then what you used last, then the order you registered commands in — so equal matches never swap places and clearing the query gives back the list you started with. Nothing scrolls on open, a new query starts at the top, and an async source keeps the highlighted command where it was. Nested pages, recents, a shortcut registry with two-step sequences, and a cheat sheet on ?.
What you get
One ranking, always the same
Match quality, then recency, then the order you registered them in. Equal matches keep their place, and clearing the query restores the original list exactly.
A list that stays still
Nothing scrolls on open, a new query starts at the top, and arrow keys scroll the least they can — the three things every palette gets wrong.
Async that behaves
Server results merge into the list, debounced, aborted and cached — and the highlighted command stays on its id when the answer lands.
Shortcuts as a feature
A registry with two-step sequences (g then p), platform-correct <kbd> rendering, a cheat sheet on ?, and no bare letter firing while you type in a field.
Nested pages
Children, or a perform() that returns commands, open a page with a breadcrumb; Backspace steps back and Escape leaves the page before the palette.
8 KB, no framework
cmdk is 17.2 KB on top of React and four Radix packages; kbar is 30.7 KB. This one runs from a script tag.
Command Palette — overview
Why it exists
Every app wants ⌘K, and the options are React components. cmdk is excellent and React-only; kbar is 31 kB; ninja-keys needs Lit and has no fuzzy search. Underneath all of them the same three complaints recur: the ranking shifts as you type, the list scrolls when you did not ask it to, and an async source moves the highlight out from under your Enter key.
What it does differently
- A documented order. Match quality, then recency, then registration order — and nothing else. Clearing the query gives back the list you started with.
- A list that stays still. Nothing scrolls on open; a new query starts at the top; arrows scroll the least they can.
- Async that behaves. Server results merge in, and the active command stays on its id.
- 8.1 kB, no framework, on a native
<dialog>. - Shortcuts as a feature, not a footnote: a registry, sequences (
g then p), platform-correct<kbd>rendering, a cheat sheet on?, and no bare letter firing while you type.
Shape of the package
| Entry | What |
|---|---|
@devix/command-palette |
createCommandPalette and the types |
@devix/command-palette/core |
ranking, recents and shortcut parsing — no DOM |
/react, /vue, /svelte, /element |
the same palette, wrapped |
/styles.css, /styles.layer.css |
the theme, plain or inside @layer devix |
Not in 1.0
Natural-language commands over your routes (an AI layer) is the Pro edition's shape. Multi-select inside the palette and drag-to-reorder of recents are deliberate omissions.
Demos
All demos →⌘K
Open it with the keyboard, search, and run a command — with the ranking and the scroll position staying put.
Nested pages
A command can open a page of its own — with a breadcrumb, Backspace to step back, and Escape that leaves the p...
Your server's results
Commands and search results in one list — debounced, aborted and cached, with the highlight staying where it w...
Shortcuts and the cheat sheet
Register a shortcut with the command it runs, including two-step ones — and press ? for the list.
How it compares
Questions
Why does the order matter so much?
Because a palette is muscle memory. If two commands match equally well and their order changes between keystrokes, you learn nothing. Here the order is documented: match quality, then recency, then registration order — and deleting what you typed gives back exactly the list you started with, which is cmdk's most-reacted open bug.
Does it work without React?
It has no framework at all. There are React, Vue and Svelte entries and a web component, but the core is plain DOM — a <script type="module"> is enough.
How many commands can it hold?
The list is windowed, so a thousand commands cost what twenty do. Use `limit` if you want to cap what is drawn, and `search()` to bring in results from your server.
Will my shortcuts fire while someone is typing?
A bare letter never fires inside an input, textarea or contenteditable. Shortcuts with a modifier still work everywhere. Sequences like “g then p” wait 1.5 seconds for the second key.
Can commands come and go?
Yes. register() returns a function that removes exactly what it added, `when()` hides a command while it does not apply, and disabled commands stay listed but greyed and unreachable by the keyboard.
How do I make it look like my app?
Every value is a CSS variable, every part takes your own classes, icons and words are replaceable, and the stylesheet ships inside @layer devix so your rules win without !important.
More from Devix
All resources →Toast
UI component
Promise-aware toasts with stacking, swipe to dismiss and correct live regions.
Confirm Dialog
UI component
Promise-based dialogs on native <dialog>: confirm, prompt and type-to-confirm.
Cookie Consent
Plugin
Blocks scripts until consent, supports Consent Mode v2, GDPR and UAE PDPL.