Free shadcn/ui command component for React — the cmdk-powered command menu and ⌘K palette. Nested pages, custom filtering, async search, and a Popover combobox. The examples official shadcn never shipped.

Installation

1 Configure registry — once per project
{
  "registries": {
    "@designrevision": "https://registry.designrevision.com/r/{name}.json"
  }
}

Add to your existing components.json. Requires shadcn/ui v2.3+.

2 Install the component

Packages

cmdk
lucide-react
utils
dialog

Props

No props documented yet.

Copied!
components/ui/command.tsx
"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,
  CommandSeparator,
  CommandShortcut,
}

Examples

Command menu

An inline menu — grouped items with icons and shortcuts, a separator, a disabled item, and a no-results state.

Packages

lucide-react
command

Props

No props documented yet.

Copied!
components/ui/command-demo-01.tsx
import {
  CalculatorIcon,
  CalendarIcon,
  CreditCardIcon,
  SettingsIcon,
  SmileIcon,
  UserIcon,
} from "lucide-react"

import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from "@/components/ui/command"

export default function CommandDemo01() {
  return (
    <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>
            <CalendarIcon />
            <span>Calendar</span>
          </CommandItem>
          <CommandItem>
            <SmileIcon />
            <span>Search Emoji</span>
          </CommandItem>
          <CommandItem disabled>
            <CalculatorIcon />
            <span>Calculator</span>
          </CommandItem>
        </CommandGroup>
        <CommandSeparator />
        <CommandGroup heading="Settings">
          <CommandItem>
            <UserIcon />
            <span>Profile</span>
            <CommandShortcut>⌘P</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <CreditCardIcon />
            <span>Billing</span>
            <CommandShortcut>⌘B</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <SettingsIcon />
            <span>Settings</span>
            <CommandShortcut>⌘S</CommandShortcut>
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </Command>
  )
}

⌘K command palette

The keyboard-driven palette — `CommandDialog` opened with a **⌘K / Ctrl+K** keydown listener, with a [Kbd](/components/kbd) hint in the trigger.

Packages

lucide-react
command
button
kbd

Props

No props documented yet.

Copied!
components/ui/command-dialog-01.tsx
"use client"

import * as React from "react"
import {
  CalendarIcon,
  CreditCardIcon,
  SettingsIcon,
  SmileIcon,
  UserIcon,
} from "lucide-react"

import { Button } from "@/components/ui/button"
import {
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from "@/components/ui/command"
import { Kbd } from "@/components/ui/kbd"

export default function CommandDialog01() {
  const [open, setOpen] = React.useState(false)

  React.useEffect(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
        setOpen((open) => !open)
      }
    }
    document.addEventListener("keydown", down)
    return () => document.removeEventListener("keydown", down)
  }, [])

  const runCommand = React.useCallback(() => setOpen(false), [])

  return (
    <>
      <Button variant="outline" onClick={() => setOpen(true)}>
        <span className="text-muted-foreground">Search…</span>
        <Kbd className="ml-2">⌘K</Kbd>
      </Button>
      <CommandDialog open={open} onOpenChange={setOpen}>
        <CommandInput placeholder="Type a command or search…" />
        <CommandList>
          <CommandEmpty>No results found.</CommandEmpty>
          <CommandGroup heading="Suggestions">
            <CommandItem onSelect={runCommand}>
              <CalendarIcon />
              <span>Calendar</span>
            </CommandItem>
            <CommandItem onSelect={runCommand}>
              <SmileIcon />
              <span>Search Emoji</span>
            </CommandItem>
          </CommandGroup>
          <CommandSeparator />
          <CommandGroup heading="Settings">
            <CommandItem onSelect={runCommand}>
              <UserIcon />
              <span>Profile</span>
              <CommandShortcut>⌘P</CommandShortcut>
            </CommandItem>
            <CommandItem onSelect={runCommand}>
              <CreditCardIcon />
              <span>Billing</span>
              <CommandShortcut>⌘B</CommandShortcut>
            </CommandItem>
            <CommandItem onSelect={runCommand}>
              <SettingsIcon />
              <span>Settings</span>
              <CommandShortcut>⌘S</CommandShortcut>
            </CommandItem>
          </CommandGroup>
        </CommandList>
      </CommandDialog>
    </>
  )
}

Nested pages

Items push **sub-pages** onto a stack; Backspace on an empty search pops back — the cmdk multi-level pattern.

Packages

command

Props

No props documented yet.

Copied!
components/ui/command-nested-01.tsx
"use client"

import * as React from "react"

import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command"

export default function CommandNested01() {
  const [pages, setPages] = React.useState<string[]>([])
  const [search, setSearch] = React.useState("")
  const page = pages[pages.length - 1]

  const push = (next: string) => {
    setPages((prev) => [...prev, next])
    setSearch("")
  }

  return (
    <Command
      className="rounded-lg border shadow-md md:min-w-[450px]"
      onKeyDown={(e) => {
        if (pages.length > 0 && (e.key === "Escape" || (e.key === "Backspace" && !search))) {
          e.preventDefault()
          setPages((prev) => prev.slice(0, -1))
        }
      }}
    >
      <CommandInput
        value={search}
        onValueChange={setSearch}
        placeholder={page ? `Search ${page}…` : "Type a command — Backspace to go back…"}
      />
      <CommandList>
        <CommandEmpty>No results found.</CommandEmpty>
        {!page && (
          <CommandGroup heading="Home">
            <CommandItem onSelect={() => push("projects")}>Search projects…</CommandItem>
            <CommandItem onSelect={() => push("teams")}>Join a team…</CommandItem>
            <CommandItem onSelect={() => push("theme")}>Change theme…</CommandItem>
          </CommandGroup>
        )}
        {page === "projects" && (
          <CommandGroup heading="Projects">
            <CommandItem>Acme Marketing Site</CommandItem>
            <CommandItem>Internal Dashboard</CommandItem>
            <CommandItem>Mobile App</CommandItem>
          </CommandGroup>
        )}
        {page === "teams" && (
          <CommandGroup heading="Teams">
            <CommandItem>Engineering</CommandItem>
            <CommandItem>Design</CommandItem>
            <CommandItem>Marketing</CommandItem>
          </CommandGroup>
        )}
        {page === "theme" && (
          <CommandGroup heading="Theme">
            <CommandItem>Light</CommandItem>
            <CommandItem>Dark</CommandItem>
            <CommandItem>System</CommandItem>
          </CommandGroup>
        )}
      </CommandList>
    </Command>
  )
}

Custom filter & keywords

Match items by aliases with a custom `filter` prop and per-item `keywords` — searching "appearance" finds "Toggle theme".

Packages

command

Props

No props documented yet.

Copied!
components/ui/command-filter-01.tsx
"use client"

import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command"

export default function CommandFilter01() {
  return (
    <Command
      className="rounded-lg border shadow-md md:min-w-[450px]"
      filter={(value, search, keywords) => {
        const haystack = `${value} ${(keywords ?? []).join(" ")}`.toLowerCase()
        return haystack.includes(search.toLowerCase()) ? 1 : 0
      }}
    >
      <CommandInput placeholder='Try "appearance" or "logout"…' />
      <CommandList>
        <CommandEmpty>No results found.</CommandEmpty>
        <CommandGroup heading="Commands">
          <CommandItem
            value="Toggle theme"
            keywords={["appearance", "dark", "light", "mode"]}
          >
            Toggle theme
          </CommandItem>
          <CommandItem value="Sign out" keywords={["logout", "exit", "leave"]}>
            Sign out
          </CommandItem>
          <CommandItem
            value="Open settings"
            keywords={["preferences", "config", "options"]}
          >
            Open settings
          </CommandItem>
          <CommandItem
            value="Invite member"
            keywords={["add", "teammate", "user"]}
          >
            Invite member
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </Command>
  )
}

Async / server search

Server-side search with `shouldFilter={false}`, a debounced request, and a loading state — you own the results.

Packages

command

Props

No props documented yet.

Copied!
components/ui/command-async-01.tsx
"use client"

import * as React from "react"

import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command"

const FRAMEWORKS = [
  "Next.js",
  "React",
  "Vue",
  "Svelte",
  "Angular",
  "Solid",
  "Astro",
  "Remix",
  "Nuxt",
  "Qwik",
]

export default function CommandAsync01() {
  const [query, setQuery] = React.useState("")
  const [results, setResults] = React.useState<string[]>([])
  const [loading, setLoading] = React.useState(false)

  React.useEffect(() => {
    if (!query) {
      setResults([])
      setLoading(false)
      return
    }
    // Simulate a debounced server request.
    setLoading(true)
    const id = setTimeout(() => {
      setResults(
        FRAMEWORKS.filter((f) => f.toLowerCase().includes(query.toLowerCase()))
      )
      setLoading(false)
    }, 500)
    return () => clearTimeout(id)
  }, [query])

  return (
    <Command
      shouldFilter={false}
      className="rounded-lg border shadow-md md:min-w-[450px]"
    >
      <CommandInput
        value={query}
        onValueChange={setQuery}
        placeholder="Search frameworks (simulated fetch)…"
      />
      <CommandList>
        {loading && (
          <div className="py-6 text-center text-sm text-muted-foreground">
            Searching…
          </div>
        )}
        {!loading && query && results.length === 0 && (
          <CommandEmpty>No results found.</CommandEmpty>
        )}
        {!loading && results.length > 0 && (
          <CommandGroup heading="Frameworks">
            {results.map((framework) => (
              <CommandItem key={framework} value={framework}>
                {framework}
              </CommandItem>
            ))}
          </CommandGroup>
        )}
      </CommandList>
    </Command>
  )
}

Combobox (Popover + Command)

The other major cmdk usage — a searchable single-select combobox built from Command inside a [Popover](/components/popover).

Packages

lucide-react
command
popover
button

Props

No props documented yet.

Copied!
components/ui/command-combobox-01.tsx
"use client"

import * as React from "react"
import { CheckIcon, ChevronsUpDownIcon } 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 CommandCombobox01() {
  const [open, setOpen] = React.useState(false)
  const [value, setValue] = React.useState("")

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <Button
          variant="outline"
          role="combobox"
          aria-expanded={open}
          className="w-[200px] justify-between"
        >
          {value
            ? frameworks.find((f) => f.value === value)?.label
            : "Select framework…"}
          <ChevronsUpDownIcon className="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)
                  }}
                >
                  <CheckIcon
                    className={cn(
                      "mr-2",
                      value === framework.value ? "opacity-100" : "opacity-0"
                    )}
                  />
                  {framework.label}
                </CommandItem>
              ))}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  )
}

Disabled & empty

Dimmed, unselectable items via the `disabled` prop, plus the `CommandEmpty` no-results message.

Packages

command

Props

No props documented yet.

Copied!
components/ui/command-disabled-01.tsx
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandShortcut,
} from "@/components/ui/command"

export default function CommandDisabled01() {
  return (
    <Command className="rounded-lg border shadow-md md:min-w-[450px]">
      <CommandInput placeholder="Type a term with no match to see the empty state…" />
      <CommandList>
        <CommandEmpty>No results found.</CommandEmpty>
        <CommandGroup heading="Actions">
          <CommandItem>
            New file
            <CommandShortcut>⌘N</CommandShortcut>
          </CommandItem>
          <CommandItem disabled>
            Save (no changes)
            <CommandShortcut>⌘S</CommandShortcut>
          </CommandItem>
          <CommandItem>
            Duplicate
            <CommandShortcut>⌘D</CommandShortcut>
          </CommandItem>
          <CommandItem disabled>Delete (locked)</CommandItem>
        </CommandGroup>
      </CommandList>
    </Command>
  )
}

Keyboard footer

An app-grade palette with a sticky footer of [Kbd](/components/kbd) hints — ↵ to select, arrows to navigate, esc to close.

Packages

lucide-react
command
kbd

Props

No props documented yet.

Copied!
components/ui/command-footer-01.tsx
import { CornerDownLeftIcon } from "lucide-react"

import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command"
import { Kbd } from "@/components/ui/kbd"

export default function CommandFooter01() {
  return (
    <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="Pages">
          <CommandItem>Dashboard</CommandItem>
          <CommandItem>Projects</CommandItem>
          <CommandItem>Team members</CommandItem>
          <CommandItem>Settings</CommandItem>
        </CommandGroup>
      </CommandList>
      <div className="flex items-center justify-between gap-2 border-t px-3 py-2 text-xs text-muted-foreground">
        <div className="flex items-center gap-1">
          <Kbd>
            <CornerDownLeftIcon className="size-3" />
          </Kbd>
          <span>to select</span>
        </div>
        <div className="flex items-center gap-1">
          <Kbd>↑</Kbd>
          <Kbd>↓</Kbd>
          <span>to navigate</span>
          <span className="mx-1">·</span>
          <Kbd>esc</Kbd>
          <span>to close</span>
        </div>
      </div>
    </Command>
  )
}

Frequently asked questions

Yes — it is the canonical shadcn/ui command, built on cmdk with the same parts including CommandDialog. Official ships the primitive with a basic menu and a ⌘K demo; we add nested pages, custom filtering with keywords, async server search, a Popover combobox, disabled/empty states, and a palette with a keyboard-hint footer.
cmdk is a fast, composable command-menu library for React by Paco Coursey — it powers the fuzzy filtering, keyboard navigation, grouping, and accessibility. The shadcn/ui Command is a thin styled wrapper over cmdk, so you get a Linear/Raycast-style palette without writing the keyboard logic yourself. Install it with npm install cmdk.
Wrap your command list in CommandDialog, hold its open state, and add a keydown listener: if e.key === "k" && (e.metaKey || e.ctrlKey), preventDefault and toggle open. CommandDialog renders the Command inside a Dialog with an sr-only title for screen readers. The ⌘K command palette example is the complete, copy-pasteable version.
cmdk filters by the item value by default. Add a keywords array to each CommandItem to match on synonyms, and/or pass a filter function to Command — filter={(value, search, keywords) => number} — returning a score (0 hides the item). The Custom filter & keywords example matches "appearance" to "Toggle theme" this way.
They are the same primitive in different containers. Put Command inside a Dialog and you get a ⌘K palette; put it inside a [Popover](/components/popover) anchored to a button and you get a Combobox (searchable select). The Combobox example shows the Popover + Command pattern; a dedicated Combobox component builds on it.
Keep a stack of page names in state. Render the root items when the stack is empty, and a sub-page's items when its name is on top; an item's onSelect pushes the next page. Pop on Escape or on Backspace when the search is empty. The Nested pages example implements exactly this.
Set shouldFilter={false} on Command so cmdk stops filtering locally, drive CommandInput as a controlled value, debounce a request on change, and render the results you get back (with a loading row in between). The Async / server search example simulates this end to end.