Command
A searchable command palette. Built on Base UI's Autocomplete primitive for filtering and keyboard navigation, and its Dialog primitive when used as a ⌘K overlay. Can also render standalone with CommandPanel, without a dialog.
Last updated August 8, 2026
CreditsModifiedPublished
Copied from shadcn/ui.
- Added RTL support
- Added a standalone CommandPanel for non-dialog usage
- Added CommandFooter for keyboard-hint helpers, hidden on small screens
Overview
"use client"
import * as React from "react"
import {
ArrowDownIcon,
ArrowUpIcon,
CalculatorIcon,
CalendarIcon,
CornerDownLeftIcon,
CreditCardIcon,
SettingsIcon,
SmileIcon,
UserIcon,
} from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Command,
CommandCollection,
CommandDialog,
CommandDialogPopup,
CommandDialogTrigger,
CommandEmpty,
CommandFooter,
CommandGroup,
CommandGroupLabel,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from "@/components/ui/command"
import { Kbd } from "@/components/ui/kbd"
const groups = [
{
value: "Suggestions",
items: [
{ value: "calendar", label: "Calendar", icon: CalendarIcon },
{ value: "search-emoji", label: "Search Emoji", icon: SmileIcon },
{ value: "calculator", label: "Calculator", icon: CalculatorIcon },
],
},
{
value: "Settings",
items: [
{ value: "profile", label: "Profile", icon: UserIcon, shortcut: "⌘P" },
{
value: "billing",
label: "Billing",
icon: CreditCardIcon,
shortcut: "⌘B",
},
{
value: "settings",
label: "Settings",
icon: SettingsIcon,
shortcut: "⌘S",
},
],
},
]
export function CommandBasicExample() {
const [open, setOpen] = React.useState(false)
React.useEffect(() => {
function down(event: KeyboardEvent) {
if (event.key === "k" && (event.metaKey || event.ctrlKey)) {
event.preventDefault()
setOpen((value) => !value)
}
}
document.addEventListener("keydown", down)
return () => document.removeEventListener("keydown", down)
}, [])
return (
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandDialogTrigger
render={
<Button variant="outline" className="justify-between text-muted-foreground">
Search commands...
<Kbd>⌘K</Kbd>
</Button>
}
/>
<CommandDialogPopup>
<Command items={groups}>
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
{groups.map((group, index) => (
<React.Fragment key={group.value}>
<CommandGroup items={group.items}>
<CommandGroupLabel>{group.value}</CommandGroupLabel>
<CommandCollection>
{(item) => (
<CommandItem key={item.value} value={item.value}>
<item.icon />
{item.label}
{"shortcut" in item && item.shortcut && (
<CommandShortcut>{item.shortcut}</CommandShortcut>
)}
</CommandItem>
)}
</CommandCollection>
</CommandGroup>
{index < groups.length - 1 && <CommandSeparator />}
</React.Fragment>
))}
</CommandList>
<CommandFooter>
<div className="flex items-center gap-3">
<span className="flex items-center gap-1">
<Kbd>
<ArrowUpIcon className="size-2.5" />
</Kbd>
<Kbd>
<ArrowDownIcon className="size-2.5" />
</Kbd>
to navigate
</span>
<span className="flex items-center gap-1">
<Kbd>
<CornerDownLeftIcon className="size-2.5" />
</Kbd>
to select
</span>
</div>
<span className="flex items-center gap-1">
<Kbd>esc</Kbd>
to close
</span>
</CommandFooter>
</Command>
</CommandDialogPopup>
</CommandDialog>
)
}Installation
$ npx shadcn@latest add https://ui.persian-labs.ir/r/command.jsonUsage
import {
Command,
CommandCollection,
CommandDialog,
CommandDialogPopup,
CommandDialogTrigger,
CommandEmpty,
CommandFooter,
CommandGroup,
CommandGroupLabel,
CommandInput,
CommandItem,
CommandList,
CommandPanel,
CommandSeparator,
CommandShortcut,
} from "@/components/ui/command"
import { Button } from "@/components/ui/button"
const items = [
{ value: "linear", label: "Linear" },
{ value: "figma", label: "Figma" },
{ value: "slack", label: "Slack" },
]
export function Example() {
return (
<CommandDialog>
<CommandDialogTrigger render={<Button variant="outline" />}>
Open Command Palette
</CommandDialogTrigger>
<CommandDialogPopup>
<Command items={items}>
<CommandInput placeholder="Search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
{items.map((item) => (
<CommandItem key={item.value} value={item.value}>
{item.label}
</CommandItem>
))}
</CommandList>
<CommandFooter>Use arrow keys to navigate, Enter to select</CommandFooter>
</Command>
</CommandDialogPopup>
</CommandDialog>
)
}Examples
Standalone panel
Render commands without a dialog by wrapping Command in CommandPanel.
"use client"
import { CalculatorIcon, CalendarIcon, SmileIcon } from "lucide-react"
import {
Command,
CommandEmpty,
CommandInput,
CommandItem,
CommandList,
CommandPanel,
} from "@/components/ui/command"
const items = [
{ value: "calendar", label: "Calendar", icon: CalendarIcon },
{ value: "search-emoji", label: "Search Emoji", icon: SmileIcon },
{ value: "calculator", label: "Calculator", icon: CalculatorIcon },
]
export function CommandPanelExample() {
return (
<CommandPanel className="w-72">
<Command items={items}>
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
{items.map((item) => (
<CommandItem key={item.value} value={item.value}>
<item.icon />
{item.label}
</CommandItem>
))}
</CommandList>
</Command>
</CommandPanel>
)
}Groups
"use client"
import * as React from "react"
import { CreditCardIcon, SettingsIcon, UserIcon } from "lucide-react"
import {
Command,
CommandCollection,
CommandEmpty,
CommandGroup,
CommandGroupLabel,
CommandInput,
CommandItem,
CommandList,
CommandPanel,
CommandSeparator,
CommandShortcut,
} from "@/components/ui/command"
const groups = [
{
value: "Suggestions",
items: [
{ value: "profile", label: "Profile", icon: UserIcon, shortcut: "⌘P" },
{
value: "billing",
label: "Billing",
icon: CreditCardIcon,
shortcut: "⌘B",
},
],
},
{
value: "Settings",
items: [
{
value: "settings",
label: "Settings",
icon: SettingsIcon,
shortcut: "⌘S",
},
],
},
]
export function CommandGroupsExample() {
return (
<CommandPanel className="w-72">
<Command items={groups}>
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
{groups.map((group, index) => (
<React.Fragment key={group.value}>
<CommandGroup items={group.items}>
<CommandGroupLabel>{group.value}</CommandGroupLabel>
<CommandCollection>
{(item) => (
<CommandItem key={item.value} value={item.value}>
<item.icon />
{item.label}
<CommandShortcut>{item.shortcut}</CommandShortcut>
</CommandItem>
)}
</CommandCollection>
</CommandGroup>
{index < groups.length - 1 && <CommandSeparator />}
</React.Fragment>
))}
</CommandList>
</Command>
</CommandPanel>
)
}RTL
No configuration needed — nest the command inside a dir="rtl" container and everything, including keyboard navigation, mirrors automatically.
"use client"
import * as React from "react"
import { FileTextIcon, SettingsIcon, UserIcon } from "lucide-react"
import {
Command,
CommandCollection,
CommandEmpty,
CommandGroup,
CommandGroupLabel,
CommandInput,
CommandItem,
CommandList,
CommandPanel,
CommandSeparator,
} from "@/components/ui/command"
const groups = [
{
value: "پیشنهادها",
items: [
{ value: "profile", label: "پروفایل کاربری", icon: UserIcon },
{ value: "documents", label: "اسناد من", icon: FileTextIcon },
],
},
{
value: "تنظیمات",
items: [{ value: "settings", label: "تنظیمات حساب", icon: SettingsIcon }],
},
]
export function CommandRtlExample() {
return (
<div dir="rtl">
<CommandPanel className="w-72">
<Command items={groups}>
<CommandInput placeholder="جستجوی دستور..." />
<CommandList>
<CommandEmpty>موردی یافت نشد.</CommandEmpty>
{groups.map((group, index) => (
<React.Fragment key={group.value}>
<CommandGroup items={group.items}>
<CommandGroupLabel>{group.value}</CommandGroupLabel>
<CommandCollection>
{(item) => (
<CommandItem key={item.value} value={item.value}>
<item.icon />
{item.label}
</CommandItem>
)}
</CommandCollection>
</CommandGroup>
{index < groups.length - 1 && <CommandSeparator />}
</React.Fragment>
))}
</CommandList>
</Command>
</CommandPanel>
</div>
)
}API Reference
Command
API reference: Base UI Command .
CommandDialog
API reference: Base UI CommandDialog .