More Modals Snippets
Command Palette — Free HTML CSS JS Cmd+K Snippet
Command Palette · Modals · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Command Palette — Cmd+K Shortcut, Grouped Commands, Fuzzy Search & Keyboard Nav

A command palette is a searchable list of application actions triggered by a keyboard shortcut — typically Cmd+K or Ctrl+K. Popularised by VS Code and Linear, it is now a standard power-user feature in developer tools, dashboards, and productivity apps. It replaces deep menu hierarchies with a single search interface: type a few characters and instantly find any action.
The COMMANDS data structure
Commands are plain JS objects with group, icon, name, desc, and shortcut fields. The render(cmds) function groups them by the group field and renders each group with a label and its items. Adding new commands is as simple as adding an object to the array.
The search/filter function
filter(q) converts the query to lowercase and calls COMMANDS.filter(c => c.name.toLowerCase().includes(lq) || c.desc.toLowerCase().includes(lq)). This searches both name and description — typing "dashboard" finds commands named "Open Dashboard" and commands with "dashboard" in their description. Passing the result to render() re-builds the list.
Keyboard navigation
A keydown listener on the input catches ArrowDown, ArrowUp, Enter, and Escape. Arrow keys call setActive(i) which removes .active from all items, adds it to the target item, and calls scrollIntoView({ block: 'nearest' }) so the active item is always visible. Enter calls pick(activeIdx) to execute the command. Escape calls closePalette().
Opening and closing
document.addEventListener('keydown', e => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); openPalette(); } }) wires the Cmd+K shortcut. openPalette() adds .show to the overlay and palette and focuses the search input with a 50ms delay (allowing the CSS transition to start first). closePalette() removes .show from both.
Replacing alert() with real actions
The pick(i) function currently calls alert(). Replace it with a switch statement or a map from command name to handler function to execute real actions.
Keyboard shortcut capture
The Cmd+K / Ctrl+K listener checks: if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); openPalette(); }. e.preventDefault() stops the browser's default Cmd+K action (some browsers map this to a bookmark or search action). The listener is attached to document once on component mount and remains active globally while the page is open.
The fuzzy search algorithm
The search filters commands by checking if the query string is a substring of the command name or description: cmd.name.toLowerCase().includes(q) || cmd.desc.toLowerCase().includes(q). For a true fuzzy match (allowing out-of-order character matching), replace includes with a character-order test: const chars = [...q]; let idx = 0; return name.split('').some(c => c === chars[idx] && ++idx) && idx === chars.length. This matches "cmpl" to "command palette".
Arrow key navigation
The keydown handler tracks selectedIndex state. ArrowDown increments it (wrapping at the list end); ArrowUp decrements (wrapping to the end). The selected command gets a highlighted background. Enter triggers the selected command's action and closes the palette. The selectedIndex is reset to 0 on each new search query.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You don't have to trace the keyboard handling and grouping logic line by line to trust it. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how setActive keeps the highlighted item in sync between the data-idx attribute, the visible array, and scrollIntoView, and why the Cmd+K listener needs to check both metaKey and ctrlKey rather than just one. The same assistant can help optimize it — for instance asking whether rebuilding the entire innerHTML string on every keystroke is fine at eleven commands but would need a different approach at a few hundred. It's also useful for extending the palette: ask it to add real fuzzy matching instead of plain substring search, support recently-used commands pinned to the top, or wire pick() to an actual router/action dispatcher instead of the placeholder alert. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a Cmd+K command palette in plain HTML, CSS, and JavaScript — no fuzzy-search library, no UI framework.
Requirements:
- A global keydown listener on document that detects both e.metaKey and e.ctrlKey combined with the "k" key, calls preventDefault to stop the browser's own shortcut, and opens the palette from anywhere on the page without requiring focus to be inside it first.
- A flat array of command objects, each with a group name, an icon, a name, a description, and an optional keyboard shortcut string, rendered by grouping commands under their group label headers dynamically (not hardcoded per-group markup).
- A search input that filters the command array by checking whether the lowercased query is a substring of either the command's name or its description, re-rendering the grouped list on every keystroke and resetting the active selection to the first result each time.
- Full keyboard navigation inside the open palette: ArrowDown and ArrowUp move a highlighted-item index up or down clamped to the list bounds, calling scrollIntoView with block: "nearest" so the highlighted item is always visible without page-level scrolling, Enter executes the highlighted command, and Escape closes the palette.
- An empty state shown when no commands match the current query.
- Open/close must be driven by toggling a class that animates opacity and a scale transform on the palette panel plus a separate blurred backdrop overlay, not by toggling display or visibility directly.
- Focus the search input automatically after opening, with enough delay that the open transition has already started.Step by step
How to Use
- 1Press Cmd+K in the previewClick inside the preview and press Cmd+K (or Ctrl+K) to open the palette. Or click the "Open palette" button.
- 2Type to searchType any word to filter commands by name or description. The list filters in real time as you type.
- 3Navigate with arrow keys and EnterUse ArrowDown/ArrowUp to move between commands. Press Enter to run the selected command. Press ESC to close.
- 4Add your own commandsIn the JS panel, add objects to the COMMANDS array: { group: "My Group", icon: "⭐", name: "My Command", desc: "What it does", shortcut: "⌘X" }.
- 5Wire pick() to real actionsReplace the alert() in pick(i) with a switch on visible[i].name or a map of command names to handler functions.
- 6Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
A document.addEventListener("keydown") handler checks (e.metaKey || e.ctrlKey) && e.key === "k". metaKey is true on Mac for the Command key; ctrlKey is true on Windows/Linux for Control. e.preventDefault() stops the browser default Cmd+K action. Then openPalette() is called.
filter(q) converts the query to lowercase and filters COMMANDS where c.name.toLowerCase().includes(lq) or c.desc.toLowerCase().includes(lq). Both name and description are searched. The filtered array is passed to render() which rebuilds the grouped list.
The keydown handler on the input catches ArrowDown (increment activeIdx), ArrowUp (decrement), and Enter (execute). setActive(i) removes .active from all items, adds it to the item with data-idx={i}, and calls el.scrollIntoView({ block: "nearest" }) to keep the active item visible.
Replace the alert() in pick(i) with your action logic. Use a switch on visible[i].name, or create an actions map: const handlers = { "Go to Home": () => router.push("/"), ... }. Then pick calls handlers[visible[i].name]?.().
Add objects to the COMMANDS array in the JS panel: { group: "MyGroup", icon: "⭐", name: "My Action", desc: "Does something useful", shortcut: "" }. The render function groups and displays them automatically.
Yes. Click "JSX" for a React component. In React, manage open state with useState, filter state with useState, and active index with useState. Use useEffect to add/remove the document keydown listener. Wire the search input to a controlled value.