1
ComponentsOverlays

Command

Command combines a dialog and autocomplete list for fast keyboard-driven navigation, search, and actions.

Purpose

Command combines a dialog and autocomplete list for keyboard-driven navigation, search, and actions. It may also render as a standalone embedded command list.

Inline command list

No command found.
import { Command, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";

export function Example() {
  return (
    <Command>
      <CommandInput aria-label="Command" placeholder="Search commands" />
      <CommandList>
        <CommandGroup><CommandItem>Open settings</CommandItem></CommandGroup>
      </CommandList>
    </Command>
  );
}

Installation

With the Siteplane registry alias configured, add the component and import the installed source from your app.

CLI

pnpm dlx shadcn@4.16.1 add @siteplane/command

Import

import { Command, CommandCollection, CommandCreateHandle, CommandDialog, CommandDialogPopup, CommandDialogTrigger, CommandEmpty, CommandFooter, CommandGroup, CommandGroupLabel, CommandInput, CommandItem, CommandList, CommandPanel, CommandSeparator, CommandShortcut } from "@/components/ui/command";

Need the machine-readable source metadata? View registry JSON.

Usage

Use Command for keyboard-first palettes and scoped action lists. Keep items grouped by task, not by implementation file.

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

export function Example() {
  return (
    <Command>
      <CommandInput aria-label="Command" placeholder="Search commands" />
      <CommandList>
        <CommandGroup><CommandItem>Open settings</CommandItem></CommandGroup>
      </CommandList>
    </Command>
  );
}

Examples

One primitive covers the palette dialog and embedded lists. The dialog brings its own shell with panel and footer; standalone lists sit in a simple bordered wrapper.

Command palette dialog

Controlled open state with a Cmd+J listener, grouped items, CommandPanel and a CommandFooter with keyboard hints.

Selected: none
const [open, setOpen] = useState(false);

useEffect(() => {
  function down(event: KeyboardEvent) {
    if (event.key === "j" && (event.metaKey || event.ctrlKey)) {
      event.preventDefault();
      setOpen((value) => !value);
    }
  }
  document.addEventListener("keydown", down);
  return () => document.removeEventListener("keydown", down);
}, []);

<CommandDialog onOpenChange={setOpen} open={open}>
  <CommandDialogTrigger render={<Button variant="outline" />}>
    Open command palette
    <KbdGroup><Kbd>⌘</Kbd><Kbd>J</Kbd></KbdGroup>
  </CommandDialogTrigger>
  <CommandDialogPopup>
    <Command items={groupedItems}>
      <CommandInput aria-label="Command" placeholder="Search for apps and commands..." />
      <CommandPanel>
        <CommandEmpty>No results found.</CommandEmpty>
        <CommandList>
          {(group: CommandGroupData, index: number) => (
            <Fragment key={group.value}>
              <CommandGroup items={group.items}>
                <CommandGroupLabel>{group.value}</CommandGroupLabel>
                <CommandCollection>
                  {(item: CommandItemData) => (
                    <CommandItem
                      key={item.value}
                      onClick={() => handleSelect(item)}
                      value={item.value}
                    >
                      <span className="flex-1">{item.label}</span>
                      {item.shortcut ? <CommandShortcut>{item.shortcut}</CommandShortcut> : null}
                    </CommandItem>
                  )}
                </CommandCollection>
              </CommandGroup>
              {index < groupedItems.length - 1 ? <CommandSeparator /> : null}
            </Fragment>
          )}
        </CommandList>
      </CommandPanel>
      <CommandFooter>
        <KbdGroup><Kbd><ArrowUpIcon /></Kbd><Kbd><ArrowDownIcon /></Kbd></KbdGroup>
        <span>Navigate</span>
        <Kbd><CornerDownLeftIcon /></Kbd>
        <span>Open</span>
        <Kbd>Esc</Kbd>
        <span>Close</span>
      </CommandFooter>
    </Command>
  </CommandDialogPopup>
</CommandDialog>

Grouped items

<Command items={groupedItems}>
  <CommandInput aria-label="Search apps and commands" placeholder="Search..." size="default" />
  <CommandEmpty>No results found.</CommandEmpty>
  <CommandList>
    {(group: CommandGroupData, index: number) => (
      <Fragment key={group.value}>
        <CommandGroup items={group.items}>
          <CommandGroupLabel>{group.value}</CommandGroupLabel>
          <CommandCollection>
            {(item: CommandItemData) => (
              <CommandItem key={item.value} value={item.value}>
                <span className="flex-1">{item.label}</span>
                {item.shortcut ? <CommandShortcut>{item.shortcut}</CommandShortcut> : null}
              </CommandItem>
            )}
          </CommandCollection>
        </CommandGroup>
        {index < groupedItems.length - 1 ? <CommandSeparator /> : null}
      </Fragment>
    )}
  </CommandList>
</Command>

Standalone command

{/* No dialog wrapper: embed the list in your own bordered shell */}
<Command items={standaloneItems}>
  <CommandInput aria-label="Search components" placeholder="Type a command..." size="default" />
  <CommandEmpty>No results found.</CommandEmpty>
  <CommandList>
    {(item: CommandItemData) => (
      <CommandItem key={item.value} value={item.value}>
        <span className="flex-1">{item.label}</span>
        {item.shortcut ? <CommandShortcut>{item.shortcut}</CommandShortcut> : null}
      </CommandItem>
    )}
  </CommandList>
</Command>

Controlled filter

Selected: none
const [query, setQuery] = useState("");
const [selected, setSelected] = useState("Selected: none");

<Command items={teamMembers}>
  <CommandInput
    aria-label="Search users"
    onChange={(event) => setQuery(event.currentTarget.value)}
    placeholder="Search users..."
    size="default"
    value={query}
  />
  <CommandEmpty>No results found.</CommandEmpty>
  <CommandList>
    {(item: CommandItemData) => (
      <CommandItem
        key={item.value}
        onClick={() => setSelected(`Selected: ${item.label}`)}
        value={item.value}
      >
        <span className="flex-1">{item.label}</span>
      </CommandItem>
    )}
  </CommandList>
</Command>

API Reference

The reference lists the source-owned exports and props that are easy to miss in visual examples. Inherited Base UI props remain available unless the wrapper narrows them.

  • CommandDialog provides modal focus management; use the standalone Command composition only when the list belongs in the page.
  • CommandCreateHandle opens or closes the dialog from detached triggers or imperative application flows.
  • autoHighlight accepts false, true, or "always" and defaults to "always", so the first result is ready for Enter.
  • Use CommandGroup, CommandSeparator, and CommandShortcut to make results scannable.
  • CommandEmpty reports no matches; CommandFooter may explain keyboard controls.

Motion

Component motion uses the shared Siteplane motion contract. See the global motion guide for provider setup, tokens and reduced-motion behavior.

  • Modal Command uses native Dialog backdrop and popup motion.
  • Filtering and highlighting come from Autocomplete; do not wrap either layer in a second transition.
  • Active items use a pointer cursor while CommandInput keeps a text cursor.

Accessibility

  • CommandInput receives focus and must have a visible or accessible name.
  • Keep keyboard navigation, Enter selection, Escape dismissal, outside click, focus trapping, and focus restoration native.
  • Every CommandItem needs a clear label. Shortcuts are supporting context, not the only action name.
  • Dialog triggers need visible text or an accessible label.

Implementation Guidance

  • Use Command for search-driven navigation and actions, not as a generic menu or select.
  • Choose modal or standalone composition deliberately.
  • Preserve native Dialog and Autocomplete focus, filtering, cursor, and motion behavior.

On This Page