A fast, keyboard-driven command palette with search, groups and shortcuts.
Calendar
Search emoji
Calculator
Profile⌘P
Billing⌘B
Settings⌘S
Pick a command with the arrow keys and Enter.
'use client'
import * as React from 'react'
import {
Command,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandSeparator,
CommandShortcut,
} from '@/components/ui/command'
interface Action {
value: string
label: string
shortcut?: string
}
const groups = [
{
value: 'Suggestions',
items: [
{ value: 'calendar', label: 'Calendar' },
{ value: 'emoji', label: 'Search emoji' },
{ value: 'calculator', label: 'Calculator' },
],
},
{
value: 'Settings',
items: [
{ value: 'profile', label: 'Profile', shortcut: '⌘P' },
{ value: 'billing', label: 'Billing', shortcut: '⌘B' },
{ value: 'settings', label: 'Settings', shortcut: '⌘S' },
],
},
]
export default function CommandDemo() {
const [last, setLast] = React.useState<string | null>(null)
return (
<div className="grid w-full max-w-md gap-3">
<Command items={groups}>
<CommandInput placeholder="Type a command or search…" />
<CommandEmpty>No results found.</CommandEmpty>
<CommandList>
{(group: (typeof groups)[number], index: number) => (
<React.Fragment key={group.value}>
{index > 0 && <CommandSeparator />}
<CommandGroup items={group.items} heading={group.value}>
{(item: Action) => (
<CommandItem key={item.value} value={item} onClick={() => setLast(item.label)}>
{item.label}
{item.shortcut && <CommandShortcut>{item.shortcut}</CommandShortcut>}
</CommandItem>
)}
</CommandGroup>
</React.Fragment>
)}
</CommandList>
</Command>
<p className="text-center text-sm text-muted-foreground" aria-live="polite">
{last ? `Ran “${last}”` : 'Pick a command with the arrow keys and Enter.'}
</p>
</div>
)
}
Built on Base UI
Installation
pnpm dlx shadcn@latest add https://kagadmodyaa.vercel.app/r/command.jsonNeeds a React + Tailwind project set up with shadcn. Copies the component source straight into your project, so you can edit it.
Command imports cn() from lib/utils.ts — add it too if you don't have it yet:
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
CommandDialog is built on Dialog. The CLI installs it for you; if you copy manually, add components/ui/dialog.tsx too.
Usage
import {
Command,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandSeparator,
CommandShortcut,
} from '@/components/ui/command'
const groups = [
{ value: 'Suggestions', items: [{ value: 'calendar', label: 'Calendar' }] },
{ value: 'Settings', items: [{ value: 'profile', label: 'Profile', shortcut: '⌘P' }] },
]
<Command items={groups}>
<CommandInput placeholder="Type a command or search…" />
<CommandEmpty>No results found.</CommandEmpty>
<CommandList>
{(group, index) => (
<Fragment key={group.value}>
{index > 0 && <CommandSeparator />}
<CommandGroup items={group.items} heading={group.value}>
{item => (
<CommandItem key={item.value} value={item} onClick={() => run(item.value)}>
{item.label}
{item.shortcut && <CommandShortcut>{item.shortcut}</CommandShortcut>}
</CommandItem>
)}
</CommandGroup>
</Fragment>
)}
</CommandList>
</Command>
// Flat list: pass an array of items and render them straight from CommandList.
// In a dialog: wrap the <Command> in <CommandDialog open={open} onOpenChange={setOpen}>.Examples
Command dialog
Press ⌘K or Ctrl+K anywhere on the page.
'use client'
import * as React from 'react'
import {
Command,
CommandDialog,
CommandInput,
CommandList,
CommandEmpty,
CommandItem,
CommandShortcut,
} from '@/components/ui/command'
import { Button } from '@/components/ui/button'
interface Action {
value: string
label: string
shortcut?: string
}
const actions: Action[] = [
{ value: 'new-file', label: 'New file', shortcut: '⌘N' },
{ value: 'open-project', label: 'Open project…', shortcut: '⌘O' },
{ value: 'search', label: 'Search in files', shortcut: '⇧⌘F' },
{ value: 'theme', label: 'Toggle theme', shortcut: '⌘T' },
{ value: 'docs', label: 'Read the docs' },
]
export default function CommandDialogExample() {
const [open, setOpen] = React.useState(false)
const [last, setLast] = React.useState<string | null>(null)
React.useEffect(() => {
const onKeyDown = (event: KeyboardEvent) => {
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') {
event.preventDefault()
setOpen(current => !current)
}
}
document.addEventListener('keydown', onKeyDown)
return () => document.removeEventListener('keydown', onKeyDown)
}, [])
return (
<div className="grid justify-items-center gap-3">
<Button variant="outline" onClick={() => setOpen(true)}>
Open command palette
<span className="ml-2 rounded border border-border px-1.5 py-0.5 text-[10px] font-normal text-muted-foreground">⌘K</span>
</Button>
<p className="text-sm text-muted-foreground" aria-live="polite">
{last ? `Ran “${last}”` : 'Press ⌘K or Ctrl+K anywhere on the page.'}
</p>
<CommandDialog open={open} onOpenChange={setOpen}>
<Command items={actions}>
<CommandInput placeholder="Type a command or search…" />
<CommandEmpty>No results found.</CommandEmpty>
<CommandList>
{(item: Action) => (
<CommandItem
key={item.value}
value={item}
onClick={() => {
setLast(item.label)
setOpen(false)
}}
>
{item.label}
{item.shortcut && <CommandShortcut>{item.shortcut}</CommandShortcut>}
</CommandItem>
)}
</CommandList>
</Command>
</CommandDialog>
</div>
)
}
API Reference
| Name | Type | Default | Description |
|---|---|---|---|
| items | Item[] | { value: string; items: Item[] }[] | — | The commands, flat or in groups. Objects shaped { value, label } are searched by label. Filtering is built in. |
| filter | (item, query, itemToString?) => boolean | — | Replace the default match (case- and accent-insensitive "contains"). |
| search / onSearchChange | string / (search: string) => void | — | Control the search text from outside. |
| defaultSearch | string | '' | Initial search text (uncontrolled). |
| CommandList children | (item, index) => ReactNode | — | A render function, called for each visible item (or group). Only the items that match the search are rendered. |
| CommandGroup items / heading | Item[] / ReactNode | — | A group of items with a label. Pass group.items and render each item with the function child. |
| CommandItem value | Item | — | The item this row represents — pass the same object you were handed. |
| CommandItem onClick | (event) => void | — | Runs on click and on Enter while the item is highlighted. Use it to perform the action. |
| CommandItem disabled | boolean | false | Skips the item when navigating and ignores clicks. |
| CommandInput aria-label | string | 'Search' | Accessible name of the search field. Change it to describe what is searched. |
| CommandEmpty | — | — | Shown only while nothing matches. Render it before CommandList. |
| CommandDialog | Dialog props + { title?, description? } | — | Hosts a Command in a modal Dialog. title and description are announced to screen readers but not shown. |
| Keyboard | — | — | Typing filters; ArrowUp and ArrowDown move the highlight, Home and End jump to the first and last item, Enter runs the highlighted item. The first match is highlighted as you type. |