ui / overlay
PREVIEW READYCommand Palette
A Spotlight-style command surface with Cmd/Ctrl K, filtering, and keyboard selection.
INSTALL / SOURCE REGISTRY
Add Command Palette to your project
The command adds the component source and its DesignOS base dependency.
$
pnpm dlx shadcn@latest add https://designos.h4rithd.com/r/command-palette.jsonregistry/default/ui/command-palette.tsx
"use client"
import * as React from "react"
import { Search } from "lucide-react"
import "@/components/designos/designos.css"
export type CommandItem = { id: string; label: string; group?: string; keywords?: string[]; icon?: React.ReactNode; shortcut?: string; onSelect: () => void }
export function CommandPalette({ items, placeholder = "Search commands…", open, onOpenChange }: { items: CommandItem[]; placeholder?: string; open?: boolean; onOpenChange?: (open: boolean) => void }) {
const dialog = React.useRef<HTMLDialogElement>(null)
const input = React.useRef<HTMLInputElement>(null)
const [query, setQuery] = React.useState("")
const [active, setActive] = React.useState(0)
const visible = React.useMemo(() => { const needle = query.toLowerCase().trim(); return needle ? items.filter((item) => [item.label, item.group, ...(item.keywords ?? [])].join(" ").toLowerCase().includes(needle)) : items }, [items, query])
React.useEffect(() => {
const shortcut = (event: KeyboardEvent) => { if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") { event.preventDefault(); if (dialog.current?.open) dialog.current.close(); else dialog.current?.showModal() } }
window.addEventListener("keydown", shortcut)
return () => window.removeEventListener("keydown", shortcut)
}, [])
React.useEffect(() => { if (open === true && !dialog.current?.open) dialog.current?.showModal(); if (open === false && dialog.current?.open) dialog.current.close() }, [open])
const choose = (item: CommandItem | undefined) => { if (!item) return; item.onSelect(); dialog.current?.close() }
return <dialog ref={dialog} className="dos-command" aria-label="Command palette" onClose={() => { setQuery(""); setActive(0); onOpenChange?.(false) }} onCancel={() => onOpenChange?.(false)} onClick={(event) => { if (event.target === dialog.current) dialog.current.close() }}>
<div className="dos-command__search"><Search aria-hidden="true" /><input ref={input} className="dos-command__input" value={query} onChange={(event) => { setQuery(event.target.value); setActive(0) }} placeholder={placeholder} aria-label={placeholder} autoFocus onKeyDown={(event) => { if (event.key === "ArrowDown") { event.preventDefault(); setActive((value) => Math.min(value + 1, visible.length - 1)) } if (event.key === "ArrowUp") { event.preventDefault(); setActive((value) => Math.max(value - 1, 0)) } if (event.key === "Enter") { event.preventDefault(); choose(visible[active]) } }} /><kbd className="dos-command__shortcut">ESC</kbd></div>
<div role="listbox" aria-label="Results" className="dos-command__list">{visible.length === 0 ? <div className="dos-command__empty">No matching commands</div> : visible.map((item, index) => <button key={item.id} role="option" aria-selected={index === active} data-active={index === active} type="button" className="dos-command__item" onPointerMove={() => setActive(index)} onClick={() => choose(item)}>{item.icon}<span>{item.label}</span>{item.shortcut && <kbd className="dos-command__shortcut">{item.shortcut}</kbd>}</button>)}</div>
</dialog>
}
Component contract
- Source-owned after installation
- Dark and light theme variables
- Keyboard and visible-focus behavior
- Reduced-motion fallback
- Responsive from 320px upward
- IBM Plex Sans + JetBrains Mono hierarchy