Command Palette
The ⌘K surface: type to filter, arrows to move, Enter to run. Built on Base UI's Autocomplete rendered inline and always open inside a Dialog — the documented composition, so filtering, roving highlight, and screen-reader wiring all come from the primitive.
tsx
"use client"
import * as React from "react"
import {
FilePlus,
GitBranch,
MessageSquarePlus,
Moon,
PanelLeft,
Search,
Settings,
TerminalSquare,
} from "lucide-react"
import { Button } from "@/registry/seam/ui/button"
import {
CommandPalette,
CommandPaletteCollection,
CommandPaletteContent,
CommandPaletteEmpty,
CommandPaletteFooter,
CommandPaletteGroup,
CommandPaletteGroupLabel,
CommandPaletteInput,
CommandPaletteItem,
CommandPaletteList,
CommandPaletteTrigger,
} from "@/registry/seam/ui/command-palette"
import { Kbd, KbdGroup } from "@/registry/seam/ui/kbd"
type Command = {
value: string
label: string
icon: React.ReactNode
shortcut?: string
}
type Group = { value: string; items: Command[] }
const GROUPS: Group[] = [
{
value: "Session",
items: [
{
value: "new-session",
label: "New session",
icon: <MessageSquarePlus />,
shortcut: "⌘N",
},
{ value: "search-sessions", label: "Search sessions", icon: <Search /> },
{
value: "toggle-sidebar",
label: "Toggle sidebar",
icon: <PanelLeft />,
shortcut: "⌘B",
},
],
},
{
value: "Workspace",
items: [
{ value: "new-worktree", label: "Create worktree", icon: <GitBranch /> },
{ value: "new-file", label: "New file", icon: <FilePlus /> },
{
value: "open-terminal",
label: "Open terminal",
icon: <TerminalSquare />,
shortcut: "⌘T",
},
{ value: "toggle-theme", label: "Toggle dark mode", icon: <Moon /> },
{
value: "settings",
label: "Settings",
icon: <Settings />,
shortcut: "⌘,",
},
],
},
]
export default function CommandPaletteDemo() {
const [open, setOpen] = React.useState(false)
return (
<CommandPalette open={open} onOpenChange={setOpen}>
<CommandPaletteTrigger
render={
<Button
variant="outline"
className="text-muted-foreground gap-3 font-normal"
/>
}
>
Search commands… <Kbd>⌘K</Kbd>
</CommandPaletteTrigger>
<CommandPaletteContent items={GROUPS}>
<CommandPaletteInput placeholder="Type a command…" />
<CommandPaletteEmpty>No commands found.</CommandPaletteEmpty>
<CommandPaletteList>
{(group: Group) => (
<CommandPaletteGroup key={group.value} items={group.items}>
<CommandPaletteGroupLabel>{group.value}</CommandPaletteGroupLabel>
<CommandPaletteCollection>
{(item: Command) => (
<CommandPaletteItem
key={item.value}
value={item}
shortcut={item.shortcut}
onClick={() => setOpen(false)}
>
{item.icon}
{item.label}
</CommandPaletteItem>
)}
</CommandPaletteCollection>
</CommandPaletteGroup>
)}
</CommandPaletteList>
<CommandPaletteFooter>
<KbdGroup>
<Kbd>↑</Kbd>
<Kbd>↓</Kbd> navigate
</KbdGroup>
<KbdGroup>
<Kbd>↵</Kbd> run
</KbdGroup>
<KbdGroup>
<Kbd>esc</Kbd> close
</KbdGroup>
</CommandPaletteFooter>
</CommandPaletteContent>
</CommandPalette>
)
}bunx --bun @seamui/cli@latest add command-paletteNotes
- The panel is a modal key rising on
condensefrom the top of the viewport; the search field is a debossed entry well; shortcut hints are embossedKbdcaps. Highlighted rows get the same accent treatment as combobox and menu items. ⌘/Ctrl+Ktoggles by default (hotkeyprop to opt out); the dialog owns mount/unmount, so the filter query, highlight, and input reset every time it closes.autoHighlightkeeps the top match armed for Enter, cmdk-style;itemsdrives Base UI's built-in filtering for flat lists and{ value, items }groups alike.- Actions are plain
onClicks onCommandPaletteItem— close the palette in the handler. No router or state-library coupling.
Press feedback, reduced motion, and haptics follow the global policy — see Motion and Haptics.