Installation
Usage
"use client"
import { useRouter } from "next/navigation"
import { CommandPalette } from "@/components/command-palette"
export function AppCommands() {
const router = useRouter()
return (
<CommandPalette
groups={[
{
heading: "Navigation",
items: [
{ id: "dashboard", label: "Go to dashboard", shortcut: ["G", "D"], onSelect: () => router.push("/dashboard") },
{ id: "billing", label: "Go to billing", keywords: ["invoice", "plan"], onSelect: () => router.push("/billing") },
],
},
]}
/>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| groups | { heading: string; items: CommandItem[] }[] | — | Sections of the menu. Items have id, label, and optional keywords, shortcut, icon, disabled and onSelect. |
| hotkey | string | false | "mod+k" | Shortcut that toggles the palette. mod is ⌘ on Mac and Ctrl elsewhere; add shift or alt, e.g. "mod+shift+p". |
| trigger | ReactNode | — | Render a search button with this text; it morphs into the palette and back. |
| triggerClassName | string | — | Classes for the trigger button. |
| open / defaultOpen / onOpenChange | boolean / boolean / (open) => void | — / false / — | Control the palette yourself, or let it manage its own state. |
| onSelect | (item: CommandItem) => void | — | Called with the chosen item after its own onSelect. The palette then closes. |
| placeholder | string | "Type a command or search…" | Placeholder of the search field. |
| label | string | "Search commands" | Accessible name of the search field and results list. |
| emptyMessage | ReactNode | "No results found." | Shown when nothing matches. |
| hints | boolean | true | Show the keyboard hints row. |
| CommandMenu | component | — | The same searchable list without the dialog, for popovers, sidebars and pages. |
Accessibility
- WAI-ARIA combobox with a listbox: focus stays in the search field and the active option is announced through aria-activedescendant.
- ↑ and ↓ move through enabled results and wrap; Enter runs the active one; disabled items are skipped and marked aria-disabled.
- Groups are labelled by their headings, and the number of results is announced politely as you type.
- Built on the native <dialog>: focus is trapped while open, Escape or a click outside closes it, and focus returns to where it was.
- Under prefers-reduced-motion the dialog and the highlight change instantly.
- The trigger is a button with aria-haspopup="dialog" and aria-expanded; the morph is replaced by a plain fade under prefers-reduced-motion.
- The highlight jumps instantly while you arrow through results and glides only after the pointer.
Changelog
- v1.0.0Latest
First release.