Command Palette
A ⌘K command palette with live fuzzy search, grouped commands, and keyboard navigation, its highlight row sliding via a shared layout.
Loading preview…
npx shadcn@latest add https://webberui.com/r/command-palette.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<CommandPalette />
Installation
npx shadcn@latest add https://webberui.com/r/command-palette.jsonOr, once registries are configured in components.json, install it as @webberui/command-palette.
Usage
import * as React from "react";
import { Home, Plus } from "lucide-react";
import { CommandPalette } from "@/components/ui/command-palette";
export function Example() {
const [open, setOpen] = React.useState(false);
const groups = [
{
heading: "Navigation",
items: [
{
id: "home",
label: "Go to home",
icon: <Home className="size-4" />,
shortcut: "G H",
onSelect: () => console.log("home"),
},
],
},
{
heading: "Actions",
items: [
{
id: "new",
label: "New document",
icon: <Plus className="size-4" />,
shortcut: "⌘ N",
onSelect: () => console.log("new"),
},
],
},
];
return <CommandPalette open={open} onOpenChange={setOpen} groups={groups} />;
}Press ⌘K (macOS) or Ctrl+K (Windows/Linux) to summon the palette at any time; you can also drive it from an external button through open / onOpenChange.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Open state in controlled mode; when omitted, the component manages it internally |
onOpenChange | (open: boolean) => void | — | Callback fired when the open state changes (in both controlled and uncontrolled modes) |
groups | CommandPaletteGroup[] | — | Command group data; each group's heading and items are rendered in order |
placeholder | string | "Type a command or search…" | Placeholder text for the search field |
className | string | — | Appended to the palette container's className |
CommandPaletteGroup
| Field | Type | Description |
|---|---|---|
heading | string | Group heading |
items | CommandPaletteItem[] | The command items in that group |
CommandPaletteItem
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier, used as the option's DOM id and the React key |
label | string | Display text, which is also what search filtering matches against |
icon | React.ReactNode | Optional leading icon |
shortcut | string | Optional keyboard shortcut hint, shown on the right of the item |
onSelect | () => void | The action to run when this item is selected (by click or Enter) |
How it works
- A global listener toggles on
⌘K/Ctrl+K; the palette is mounted ontodocument.bodywithcreatePortal. - The overlay fades in and the palette scales and fades in, centered slightly above the middle; the search field focuses automatically and typing applies live subsequence fuzzy filtering.
- The up and down keys cycle the highlight through the filtered items,
Enterruns the item,Esccloses, and clicking the overlay closes. - The highlight row's background slides smoothly between items via a shared
layoutId.
Accessibility
- The palette is
role="dialog"witharia-modal="true"; it locks body scrolling while open and returns focus to the previously focused element after closing. - The search field is
role="combobox"and points at the currently highlighted item witharia-activedescendant; the results arerole="listbox"and the items arerole="option"witharia-selected. - When the user has "reduce motion" enabled at the system level, the palette only fades in and out by opacity and the highlight row snaps into place, with no scaling or sliding.