Command
React command palette with fuzzy search, keyboard shortcuts, and dialog support. Perfect for app navigation, search interfaces, and power user workflows.
Building a command palette that feels like VS Code or Linear? Users love hitting ⌘K to instantly search and navigate. The Command component delivers that smooth, fast experience with fuzzy search, keyboard navigation, and the flexibility to work as both an inline menu and modal dialog.
import { Calculator, Calendar, CreditCard, Settings, Smile, User,} from "lucide-react"import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut,} from "@/components/ui/command"export default function CommandDemo() { return ( <div className="w-full p-6 flex justify-center"> <Command className="rounded-lg border shadow-md md:min-w-[450px]"> <CommandInput placeholder="Type a command or search..." /> <CommandList> <CommandEmpty>No results found.</CommandEmpty> <CommandGroup heading="Suggestions"> <CommandItem> <Calendar /> <span>Calendar</span> </CommandItem> <CommandItem> <Smile /> <span>Search Emoji</span> </CommandItem> <CommandItem disabled> <Calculator /> <span>Calculator</span> </CommandItem> </CommandGroup> <CommandSeparator /> <CommandGroup heading="Settings"> <CommandItem> <User /> <span>Profile</span> <CommandShortcut>⌘P</CommandShortcut> </CommandItem> <CommandItem> <CreditCard /> <span>Billing</span> <CommandShortcut>⌘B</CommandShortcut> </CommandItem> <CommandItem> <Settings /> <span>Settings</span> <CommandShortcut>⌘S</CommandShortcut> </CommandItem> </CommandGroup> </CommandList> </Command> </div> )}
"use client"import * as React from "react"import { Command as CommandPrimitive } from "cmdk"import { SearchIcon } from "lucide-react"import { cn } from "@/lib/utils"import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,} from "@/components/ui/dialog"function Command({ className, ...props}: React.ComponentProps<typeof CommandPrimitive>) { return ( <CommandPrimitive data-slot="command" className={cn( "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md", className )} {...props} /> )}function CommandDialog({ title = "Command Palette", description = "Search for a command to run...", children, className, showCloseButton = true, ...props}: React.ComponentProps<typeof Dialog> & { title?: string description?: string className?: string showCloseButton?: boolean}) { return ( <Dialog {...props}> <DialogHeader className="sr-only"> <DialogTitle>{title}</DialogTitle> <DialogDescription>{description}</DialogDescription> </DialogHeader> <DialogContent className={cn("overflow-hidden p-0", className)} showCloseButton={showCloseButton} > <Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> {children} </Command> </DialogContent> </Dialog> )}function CommandInput({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Input>) { return ( <div data-slot="command-input-wrapper" className="flex h-9 items-center gap-2 border-b px-3" > <SearchIcon className="size-4 shrink-0 opacity-50" /> <CommandPrimitive.Input data-slot="command-input" className={cn( "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50", className )} {...props} /> </div> )}function CommandList({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.List>) { return ( <CommandPrimitive.List data-slot="command-list" className={cn( "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className )} {...props} /> )}function CommandEmpty({ ...props}: React.ComponentProps<typeof CommandPrimitive.Empty>) { return ( <CommandPrimitive.Empty data-slot="command-empty" className="py-6 text-center text-sm" {...props} /> )}function CommandGroup({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Group>) { return ( <CommandPrimitive.Group data-slot="command-group" className={cn( "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium", className )} {...props} /> )}function CommandSeparator({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Separator>) { return ( <CommandPrimitive.Separator data-slot="command-separator" className={cn("bg-border -mx-1 h-px", className)} {...props} /> )}function CommandItem({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Item>) { return ( <CommandPrimitive.Item data-slot="command-item" className={cn( "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function CommandShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="command-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", className )} {...props} /> )}export { Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator,}
Built on cmdk by pacocoursey - the same library powering command palettes in top developer tools.
npx shadcn@latest add command
Command patterns that users expect
Quick search and navigation
The classic inline command menu for filtering through options:
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList,} from "@/components/ui/command"export default function CommandSimple() { return ( <div className="w-full p-6 flex justify-center"> <Command className="rounded-lg border shadow-md w-[350px]"> <CommandInput placeholder="Type a command or search..." /> <CommandList> <CommandEmpty>No results found.</CommandEmpty> <CommandGroup heading="Suggestions"> <CommandItem>Calendar</CommandItem> <CommandItem>Search Emoji</CommandItem> <CommandItem>Calculator</CommandItem> </CommandGroup> </CommandList> </Command> </div> )}
"use client"import * as React from "react"import { Command as CommandPrimitive } from "cmdk"import { SearchIcon } from "lucide-react"import { cn } from "@/lib/utils"import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,} from "@/components/ui/dialog"function Command({ className, ...props}: React.ComponentProps<typeof CommandPrimitive>) { return ( <CommandPrimitive data-slot="command" className={cn( "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md", className )} {...props} /> )}function CommandDialog({ title = "Command Palette", description = "Search for a command to run...", children, className, showCloseButton = true, ...props}: React.ComponentProps<typeof Dialog> & { title?: string description?: string className?: string showCloseButton?: boolean}) { return ( <Dialog {...props}> <DialogHeader className="sr-only"> <DialogTitle>{title}</DialogTitle> <DialogDescription>{description}</DialogDescription> </DialogHeader> <DialogContent className={cn("overflow-hidden p-0", className)} showCloseButton={showCloseButton} > <Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> {children} </Command> </DialogContent> </Dialog> )}function CommandInput({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Input>) { return ( <div data-slot="command-input-wrapper" className="flex h-9 items-center gap-2 border-b px-3" > <SearchIcon className="size-4 shrink-0 opacity-50" /> <CommandPrimitive.Input data-slot="command-input" className={cn( "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50", className )} {...props} /> </div> )}function CommandList({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.List>) { return ( <CommandPrimitive.List data-slot="command-list" className={cn( "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className )} {...props} /> )}function CommandEmpty({ ...props}: React.ComponentProps<typeof CommandPrimitive.Empty>) { return ( <CommandPrimitive.Empty data-slot="command-empty" className="py-6 text-center text-sm" {...props} /> )}function CommandGroup({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Group>) { return ( <CommandPrimitive.Group data-slot="command-group" className={cn( "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium", className )} {...props} /> )}function CommandSeparator({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Separator>) { return ( <CommandPrimitive.Separator data-slot="command-separator" className={cn("bg-border -mx-1 h-px", className)} {...props} /> )}function CommandItem({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Item>) { return ( <CommandPrimitive.Item data-slot="command-item" className={cn( "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function CommandShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="command-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", className )} {...props} /> )}export { Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator,}
Global command palette
Hit a shortcut key to open a full-screen command interface:
"use client"import * as React from "react"import { Calculator, Calendar, CreditCard, Settings, Smile, User,} from "lucide-react"import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut,} from "@/components/ui/command"export default function CommandDialogDemo() { const [open, setOpen] = React.useState(false) React.useEffect(() => { const down = (e: KeyboardEvent) => { if (e.key === "j" && (e.metaKey || e.ctrlKey)) { e.preventDefault() setOpen((open) => !open) } } document.addEventListener("keydown", down) return () => document.removeEventListener("keydown", down) }, []) return ( <div className="w-full p-6 flex justify-center"> <div className="text-center space-y-4"> <p className="text-muted-foreground text-sm"> Press{" "} <kbd className="bg-muted text-muted-foreground pointer-events-none inline-flex h-5 items-center gap-1 rounded border px-1.5 font-mono text-[10px] font-medium opacity-100 select-none"> <span className="text-xs">⌘</span>J </kbd> </p> <CommandDialog open={open} onOpenChange={setOpen}> <CommandInput placeholder="Type a command or search..." /> <CommandList> <CommandEmpty>No results found.</CommandEmpty> <CommandGroup heading="Suggestions"> <CommandItem> <Calendar /> <span>Calendar</span> </CommandItem> <CommandItem> <Smile /> <span>Search Emoji</span> </CommandItem> <CommandItem> <Calculator /> <span>Calculator</span> </CommandItem> </CommandGroup> <CommandSeparator /> <CommandGroup heading="Settings"> <CommandItem> <User /> <span>Profile</span> <CommandShortcut>⌘P</CommandShortcut> </CommandItem> <CommandItem> <CreditCard /> <span>Billing</span> <CommandShortcut>⌘B</CommandShortcut> </CommandItem> <CommandItem> <Settings /> <span>Settings</span> <CommandShortcut>⌘S</CommandShortcut> </CommandItem> </CommandGroup> </CommandList> </CommandDialog> </div> </div> )}
"use client"import * as React from "react"import { Command as CommandPrimitive } from "cmdk"import { SearchIcon } from "lucide-react"import { cn } from "@/lib/utils"import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,} from "@/components/ui/dialog"function Command({ className, ...props}: React.ComponentProps<typeof CommandPrimitive>) { return ( <CommandPrimitive data-slot="command" className={cn( "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md", className )} {...props} /> )}function CommandDialog({ title = "Command Palette", description = "Search for a command to run...", children, className, showCloseButton = true, ...props}: React.ComponentProps<typeof Dialog> & { title?: string description?: string className?: string showCloseButton?: boolean}) { return ( <Dialog {...props}> <DialogHeader className="sr-only"> <DialogTitle>{title}</DialogTitle> <DialogDescription>{description}</DialogDescription> </DialogHeader> <DialogContent className={cn("overflow-hidden p-0", className)} showCloseButton={showCloseButton} > <Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> {children} </Command> </DialogContent> </Dialog> )}function CommandInput({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Input>) { return ( <div data-slot="command-input-wrapper" className="flex h-9 items-center gap-2 border-b px-3" > <SearchIcon className="size-4 shrink-0 opacity-50" /> <CommandPrimitive.Input data-slot="command-input" className={cn( "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50", className )} {...props} /> </div> )}function CommandList({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.List>) { return ( <CommandPrimitive.List data-slot="command-list" className={cn( "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className )} {...props} /> )}function CommandEmpty({ ...props}: React.ComponentProps<typeof CommandPrimitive.Empty>) { return ( <CommandPrimitive.Empty data-slot="command-empty" className="py-6 text-center text-sm" {...props} /> )}function CommandGroup({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Group>) { return ( <CommandPrimitive.Group data-slot="command-group" className={cn( "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium", className )} {...props} /> )}function CommandSeparator({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Separator>) { return ( <CommandPrimitive.Separator data-slot="command-separator" className={cn("bg-border -mx-1 h-px", className)} {...props} /> )}function CommandItem({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Item>) { return ( <CommandPrimitive.Item data-slot="command-item" className={cn( "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function CommandShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="command-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", className )} {...props} /> )}export { Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator,}
Smart search with filtering
Let users search through dynamic content with instant results:
"use client"import * as React from "react"import { FileIcon, FolderIcon, ImageIcon, VideoIcon, MusicIcon,} from "lucide-react"import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList,} from "@/components/ui/command"const files = [ { name: "README.md", type: "file", icon: FileIcon }, { name: "src", type: "folder", icon: FolderIcon }, { name: "package.json", type: "file", icon: FileIcon }, { name: "components", type: "folder", icon: FolderIcon }, { name: "image.png", type: "image", icon: ImageIcon }, { name: "video.mp4", type: "video", icon: VideoIcon }, { name: "music.mp3", type: "audio", icon: MusicIcon }, { name: "utils", type: "folder", icon: FolderIcon }, { name: "styles.css", type: "file", icon: FileIcon },]export default function CommandSearch() { const [search, setSearch] = React.useState("") const filteredFiles = files.filter(file => file.name.toLowerCase().includes(search.toLowerCase()) ) return ( <div className="w-full p-6 flex justify-center"> <Command className="rounded-lg border shadow-md w-[400px]"> <CommandInput placeholder="Search files..." value={search} onValueChange={setSearch} /> <CommandList> <CommandEmpty>No files found.</CommandEmpty> <CommandGroup heading={`Files (${filteredFiles.length})`}> {filteredFiles.map((file) => { const Icon = file.icon return ( <CommandItem key={file.name}> <Icon /> <span>{file.name}</span> <span className="ml-auto text-xs text-muted-foreground"> {file.type} </span> </CommandItem> ) })} </CommandGroup> </CommandList> </Command> </div> )}
"use client"import * as React from "react"import { Command as CommandPrimitive } from "cmdk"import { SearchIcon } from "lucide-react"import { cn } from "@/lib/utils"import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,} from "@/components/ui/dialog"function Command({ className, ...props}: React.ComponentProps<typeof CommandPrimitive>) { return ( <CommandPrimitive data-slot="command" className={cn( "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md", className )} {...props} /> )}function CommandDialog({ title = "Command Palette", description = "Search for a command to run...", children, className, showCloseButton = true, ...props}: React.ComponentProps<typeof Dialog> & { title?: string description?: string className?: string showCloseButton?: boolean}) { return ( <Dialog {...props}> <DialogHeader className="sr-only"> <DialogTitle>{title}</DialogTitle> <DialogDescription>{description}</DialogDescription> </DialogHeader> <DialogContent className={cn("overflow-hidden p-0", className)} showCloseButton={showCloseButton} > <Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> {children} </Command> </DialogContent> </Dialog> )}function CommandInput({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Input>) { return ( <div data-slot="command-input-wrapper" className="flex h-9 items-center gap-2 border-b px-3" > <SearchIcon className="size-4 shrink-0 opacity-50" /> <CommandPrimitive.Input data-slot="command-input" className={cn( "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50", className )} {...props} /> </div> )}function CommandList({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.List>) { return ( <CommandPrimitive.List data-slot="command-list" className={cn( "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className )} {...props} /> )}function CommandEmpty({ ...props}: React.ComponentProps<typeof CommandPrimitive.Empty>) { return ( <CommandPrimitive.Empty data-slot="command-empty" className="py-6 text-center text-sm" {...props} /> )}function CommandGroup({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Group>) { return ( <CommandPrimitive.Group data-slot="command-group" className={cn( "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium", className )} {...props} /> )}function CommandSeparator({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Separator>) { return ( <CommandPrimitive.Separator data-slot="command-separator" className={cn("bg-border -mx-1 h-px", className)} {...props} /> )}function CommandItem({ className, ...props}: React.ComponentProps<typeof CommandPrimitive.Item>) { return ( <CommandPrimitive.Item data-slot="command-item" className={cn( "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className )} {...props} /> )}function CommandShortcut({ className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="command-shortcut" className={cn( "text-muted-foreground ml-auto text-xs tracking-widest", className )} {...props} /> )}export { Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator,}
Dropdown alternatives
Replace traditional select dropdowns with searchable command interfaces:
"use client"import * as React from "react"import { Check, ChevronsUpDown } from "lucide-react"import { cn } from "@/lib/utils"import { Button } from "@/components/ui/button"import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList,} from "@/components/ui/command"import { Popover, PopoverContent, PopoverTrigger,} from "@/components/ui/popover"const frameworks = [ { value: "next.js", label: "Next.js" }, { value: "sveltekit", label: "SvelteKit" }, { value: "nuxt.js", label: "Nuxt.js" }, { value: "remix", label: "Remix" }, { value: "astro", label: "Astro" },]export default function CommandCombobox() { const [open, setOpen] = React.useState(false) const [value, setValue] = React.useState("") return ( <div className="w-full p-6 flex justify-center"> <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger asChild> <Button variant="outline" role="combobox" aria-expanded={open} className="w-[200px] justify-between" > {value ? frameworks.find((framework) => framework.value === value)?.label : "Select framework..."} <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> </Button> </PopoverTrigger> <PopoverContent className="w-[200px] p-0"> <Command> <CommandInput placeholder="Search framework..." /> <CommandList> <CommandEmpty>No framework found.</CommandEmpty> <CommandGroup> {frameworks.map((framework) => ( <CommandItem key={framework.value} value={framework.value} onSelect={(currentValue) => { setValue(currentValue === value ? "" : currentValue) setOpen(false) }} > <Check className={cn( "mr-2 h-4 w-4", value === framework.value ? "opacity-100" : "opacity-0" )} /> {framework.label} </CommandItem> ))} </CommandGroup> </CommandList> </Command> </PopoverContent> </Popover> </div> )}
import * as React from "react"import { Slot } from "@radix-ui/react-slot"import { cva, type VariantProps } from "class-variance-authority"import { cn } from "@/lib/utils"const buttonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", { variants: { variant: { default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", link: "text-primary underline-offset-4 hover:underline", }, size: { default: "h-9 px-4 py-2 has-[>svg]:px-3", sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", lg: "h-10 rounded-md px-6 has-[>svg]:px-4", icon: "size-9", }, }, defaultVariants: { variant: "default", size: "default", }, })function Button({ className, variant, size, asChild = false, ...props}: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & { asChild?: boolean }) { const Comp = asChild ? Slot : "button" return ( <Comp data-slot="button" className={cn(buttonVariants({ variant, size, className }))} {...props} /> )}export { Button, buttonVariants }
What components can you use?
Main Components
Component | What it does |
---|---|
Command | Root container that handles search and keyboard navigation |
CommandInput | Search input with built-in icon |
CommandList | Scrollable container for all menu items |
CommandItem | Individual selectable menu item |
CommandEmpty | Shows when no search results found |
CommandGroup | Groups related items with optional heading |
CommandSeparator | Visual divider between sections |
Dialog & Advanced
Component | What it does |
---|---|
CommandDialog | Full-screen command palette with backdrop |
CommandShortcut | Displays keyboard shortcuts like ⌘K |
Command best practices
What makes command palettes feel fast and intuitive:
- Use clear action verbs - "Create Project" instead of "New"
- Group logically - Navigation, Actions, Settings as separate sections
- Show keyboard shortcuts - Display ⌘K, ⌘N to help power users
- Handle empty states - "No results found" when search returns nothing
- Keep search fast - Debounce API calls, filter locally when possible
- Test keyboard navigation - Arrow keys, Enter, Escape should all work
- Make shortcuts discoverable - Show the ⌘K shortcut somewhere in your UI
- Return focus properly - When closing dialog, focus goes back to trigger
Combobox
React combobox with search, autocomplete, and keyboard navigation. Perfect for dropdowns with many options, user selection, and searchable interfaces.
Context Menu
React context menu with right-click actions, submenus, and keyboard navigation. Perfect for file managers, editors, and interactive interfaces.