# Command

> Search and run actions from an inline list or command palette.

Source: https://www.honestui.com/docs/components/command

```tsx
"use client"

import * as React from "react"
import {
  File as FileIcon,
  Palette as PaletteIcon,
  Search as SearchIcon,
  Settings as SettingsIcon,
  User as UserIcon,
} from "honestui/icons"

import {
  Command,
  CommandContent,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandLabel,
  CommandSeparator,
} from "@/components/honest-ui/ui/command"

export function CommandDemo() {
  const [lastAction, setLastAction] = React.useState("No command selected")

  return (
    <div className="w-full max-w-lg space-y-3">
      <Command className="rounded-sm border-[0.5px] border-border shadow-sm">
        <CommandInput
          leadingIcon={<SearchIcon />}
          placeholder="Search commands..."
        />
        <CommandContent>
          <CommandEmpty>No matching commands.</CommandEmpty>
          <CommandGroup>
            <CommandLabel>Navigation</CommandLabel>
            <CommandItem
              leadingIcon={<FileIcon />}
              trailingIcon={<span className="text-xs">G then D</span>}
              onClick={() => setLastAction("Opened documents")}
            >
              Open documents
            </CommandItem>
            <CommandItem
              leadingIcon={<UserIcon />}
              onClick={() => setLastAction("Opened account")}
            >
              Open account
            </CommandItem>
          </CommandGroup>
          <CommandSeparator />
          <CommandGroup>
            <CommandLabel>Preferences</CommandLabel>
            <CommandItem
              leadingIcon={<PaletteIcon />}
              onClick={() => setLastAction("Opened appearance")}
            >
              Change appearance
            </CommandItem>
            <CommandItem
              leadingIcon={<SettingsIcon />}
              onClick={() => setLastAction("Opened settings")}
            >
              Open settings
            </CommandItem>
          </CommandGroup>
        </CommandContent>
      </Command>
      <p className="text-center text-xs text-muted-foreground" aria-live="polite">
        {lastAction}
      </p>
    </div>
  )
}

```

## Overview [#overview]

Use Command to help people find and run actions from a compact, keyboard-oriented list. It works inline as a persistent command browser or inside `CommandDialog` as a temporary command palette summoned with a shortcut such as <kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>K</kbd>.

Command is built on Base UI Autocomplete, so the input and results share highlight state and keyboard navigation. Use it for actions — opening a document, navigating to a workspace area, changing a setting. Use Combobox instead when the person is selecting a value for a field rather than running an action; the interaction feels similar but the outcome is data entry, and Combobox integrates with Field and forms.

## Anatomy [#anatomy]

Command includes the root panel, search input with optional leading icon, scrollable content area, items with optional leading and trailing icons, an empty state, optional groups with labels, and separators. The dialog variant wraps the same pieces in a root, trigger, viewport, and popup with a visually hidden title.

All parts and Tailwind styles live in one `command.tsx` file and use named PascalCase exports. The content area caps at 320 px tall and scrolls internally, so a long command list never grows the page or the popup beyond its viewport.

## Behavior [#behavior]

Typing filters items. When you render `CommandItem` elements directly (the JSX style), Honest UI filters them by their `value` prop or visible text using a case-insensitive substring match. When you supply the root `items` prop instead, Base UI owns filtering and supports object values through `itemToStringValue`.

The first result is highlighted by default (`autoHighlight="always"`), so <kbd>Enter</kbd> always has a target. Groups, labels, and separators disappear during JSX filtering so results form one continuous list rather than scattered empty sections. The empty state appears only when no option remains.

Keep item labels action-oriented verbs ("Open documents", "Invite member"). Use the trailing icon slot for concise keyboard hints, not secondary descriptions that truncate on narrow panels.

## Keyboard [#keyboard]

| Key                                       | Result                                                                         |
| ----------------------------------------- | ------------------------------------------------------------------------------ |
| <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd> | Move the highlight through results                                             |
| <kbd>Enter</kbd>                          | Runs the highlighted item                                                      |
| <kbd>Escape</kbd>                         | Clears the query when the list is closed; closes the dialog variant while open |
| <kbd>Home</kbd> / <kbd>End</kbd>          | Move the text cursor within the query                                          |

Highlighting uses the combobox pattern: focus stays in the input while the highlighted option is announced through `aria-activedescendant`, so typing never loses focus.

## Accessibility [#accessibility]

`CommandInput` renders `type="search"` with role `combobox` and the default accessible name "Search commands." Pass a more specific `aria-label` when the scope is narrower, such as "Search billing actions," so screen-reader users know what the results will do. Results live in a `listbox`; each item is an `option` with `aria-selected` state managed for you.

The dialog content includes a visually hidden title (default "Command palette"); change it through the `title` prop so screen-reader users hear the same scope sighted users see in the placeholder.

If you add a global shortcut to open the palette, follow the pattern in the [command dialog example](#command-dialog): support both Control and Command, call `preventDefault` only for that shortcut, remove the listener when the component unmounts, and always keep a visible trigger — people who have never seen the shortcut need a discoverable way in.

The empty state matters here too: a blank panel after a typo reads as breakage. See [Don't do this](#dont-do-this).

Colors come from theme tokens, so the panel, highlight, and placeholder adapt to dark mode automatically. Item labels truncate rather than wrap; keep them short enough to survive narrow panels and localized text expansion.

## Installation [#installation]


  

  
    <CliBlock commands="[&#x22;command&#x22;]" />
  

  
    
      
        Install the required dependency:
      

      ```bash
      npm install @base-ui/react
      ```

      
        Copy and paste the component into your project.
      

      ### components/ui/command.tsx

```tsx
"use client"

import * as React from "react"
import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete"
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"

import { cn } from "@/lib/utils"

type CommandContextValue = {
  inputValue: string
  usesItemsProp: boolean
}

const CommandContext = React.createContext<CommandContextValue | null>(null)

function useCommandContext() {
  const context = React.useContext(CommandContext)

  if (!context) {
    throw new Error("Command parts must be used within Command")
  }

  return context
}

function getTextContent(node: React.ReactNode): string {
  return React.Children.toArray(node)
    .map((child) => {
      if (typeof child === "string" || typeof child === "number") {
        return String(child)
      }

      if (React.isValidElement<{ children?: React.ReactNode }>(child)) {
        return getTextContent(child.props.children)
      }

      return ""
    })
    .join(" ")
}

function matchesSearch(
  value: unknown,
  children: React.ReactNode,
  query: string
) {
  const searchableValue =
    typeof value === "string" ? value : getTextContent(children)

  return searchableValue
    .toLocaleLowerCase()
    .includes(query.trim().toLocaleLowerCase())
}

type CommandProps<ItemValue = string> = Omit<
  AutocompletePrimitive.Root.Props<ItemValue>,
  "items"
> & {
  className?: string
  items?: readonly ItemValue[]
}

function Command<ItemValue = string>({
  value: controlledValue,
  defaultValue,
  onValueChange,
  items,
  inline = true,
  open = true,
  autoHighlight = "always",
  keepHighlight = true,
  className,
  children,
  ...props
}: CommandProps<ItemValue>) {
  const [uncontrolledValue, setUncontrolledValue] = React.useState(
    typeof defaultValue === "string" ? defaultValue : ""
  )
  const inputValue =
    typeof controlledValue === "string" ? controlledValue : uncontrolledValue

  const handleValueChange = React.useCallback<
    NonNullable<AutocompletePrimitive.Root.Props<ItemValue>["onValueChange"]>
  >(
    (nextValue, eventDetails) => {
      if (controlledValue === undefined) {
        setUncontrolledValue(nextValue)
      }
      onValueChange?.(nextValue, eventDetails)
    },
    [controlledValue, onValueChange]
  )

  const context = React.useMemo<CommandContextValue>(
    () => ({
      inputValue,
      usesItemsProp: items !== undefined,
    }),
    [inputValue, items]
  )

  return (
    <CommandContext.Provider value={context}>
      <AutocompletePrimitive.Root<ItemValue>
        inline={inline}
        open={open}
        autoHighlight={autoHighlight}
        keepHighlight={keepHighlight}
        value={controlledValue}
        defaultValue={defaultValue}
        onValueChange={handleValueChange}
        items={items}
        {...props}
      >
        <div
          data-slot="command"
          className={cn(
            "isolate flex w-full flex-col overflow-hidden bg-[var(--hui-color-background-base-primary)]",
            className
          )}
        >
          {children}
        </div>
      </AutocompletePrimitive.Root>
    </CommandContext.Provider>
  )
}

type CommandInputProps = Omit<AutocompletePrimitive.Input.Props, "size"> & {
  leadingIcon?: React.ReactNode
  size?: "sm" | "default" | "lg"
}

function CommandInput({
  className,
  leadingIcon,
  size = "lg",
  placeholder = "Search...",
  autoFocus = true,
  "aria-label": ariaLabel = "Search commands",
  ...props
}: CommandInputProps) {
  return (
    <div
      data-slot="command-input-container"
      className="flex flex-col bg-[var(--hui-color-background-base-primary)] p-[var(--hui-space-2)]"
    >
      <div className="flex items-center gap-[var(--hui-space-2)] rounded-[var(--hui-radius-2)] border-0 bg-transparent px-[var(--hui-space-3)] shadow-none outline-none forced-colors:focus-within:outline forced-colors:focus-within:outline-1 forced-colors:focus-within:outline-offset-[-2px]">
        {leadingIcon ? (
          <span
            aria-hidden="true"
            className="flex shrink-0 items-center justify-center text-[var(--hui-color-foreground-base-secondary)] [&_svg]:size-[var(--hui-space-5)]"
          >
            {leadingIcon}
          </span>
        ) : null}
        <AutocompletePrimitive.Input
          data-slot="command-input"
          type="search"
          aria-label={ariaLabel}
          autoFocus={autoFocus}
          placeholder={placeholder}
          className={cn(
            "m-0 w-full min-w-0 border-0 bg-transparent p-0 text-[var(--hui-color-foreground-base-primary)] shadow-none outline-none [font-size:var(--hui-font-size-control,13px)] [font-weight:var(--hui-font-weight-regular)] [letter-spacing:var(--hui-letter-spacing-control,-0.003em)] [line-height:var(--hui-line-height-control,16px)] placeholder:text-[var(--hui-color-foreground-base-tertiary)] focus-visible:ring-0 focus-visible:outline-none [&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none",
            size === "sm" && "h-[var(--hui-space-7)]",
            size === "default" && "h-[var(--hui-space-9)]",
            size === "lg" && "h-[var(--hui-space-11)]",
            className
          )}
          {...props}
        />
      </div>
    </div>
  )
}

type CommandContentProps = AutocompletePrimitive.List.Props

function CommandContent({ className, ...props }: CommandContentProps) {
  return (
    <AutocompletePrimitive.List
      data-slot="command-content"
      className={cn(
        "flex max-h-80 flex-col overflow-x-hidden overflow-y-auto overscroll-contain px-[var(--hui-space-2)] empty:p-0 [&:has([role=option])>[data-slot=command-empty]]:hidden [&:not(:has([data-slot=command-group]))]:gap-[var(--hui-space-1)] [&:not(:has([data-slot=command-group]))]:py-[var(--hui-space-3)]",
        className
      )}
      {...props}
    />
  )
}

type CommandItemProps = AutocompletePrimitive.Item.Props & {
  leadingIcon?: React.ReactNode
  trailingIcon?: React.ReactNode
}

function CommandItem({
  className,
  children,
  value: providedValue,
  leadingIcon,
  trailingIcon,
  ...props
}: CommandItemProps) {
  const { inputValue, usesItemsProp } = useCommandContext()
  const value =
    providedValue !== undefined
      ? providedValue
      : typeof children === "string"
        ? children
        : undefined

  if (
    !usesItemsProp &&
    inputValue &&
    !matchesSearch(value, children, inputValue)
  ) {
    return null
  }

  return (
    <AutocompletePrimitive.Item
      data-slot="command-item"
      value={value}
      className={cn(
        "flex min-h-9 cursor-pointer scroll-my-[var(--hui-space-2)] items-center gap-[var(--hui-space-3)] rounded-[var(--hui-radius-2)] bg-[var(--hui-color-background-base-primary)] p-[var(--hui-space-3)] text-[var(--hui-color-foreground-base-primary)] outline-none select-none [font-size:var(--hui-font-size-control,13px)] [font-weight:var(--hui-font-weight-regular)] [letter-spacing:var(--hui-letter-spacing-control,-0.003em)] [line-height:var(--hui-line-height-control,16px)] data-disabled:pointer-events-none data-disabled:text-[var(--hui-color-foreground-base-tertiary)] data-highlighted:bg-[var(--hui-color-background-base-primary-hover)]",
        className
      )}
      {...props}
    >
      {leadingIcon ? (
        <span
          data-slot="command-item-leading-icon"
          aria-hidden="true"
          className="inline-flex shrink-0 items-center justify-center text-[var(--hui-color-foreground-base-secondary)] [&_svg]:size-[var(--hui-space-5)]"
        >
          {leadingIcon}
        </span>
      ) : null}
      <span
        data-slot="command-item-label"
        className="min-w-0 flex-1 truncate"
      >
        {children}
      </span>
      {trailingIcon ? (
        <span
          data-slot="command-item-trailing-icon"
          className="ms-auto inline-flex shrink-0 items-center justify-center text-[var(--hui-color-foreground-base-secondary)] [&_svg]:size-[var(--hui-space-5)]"
        >
          {trailingIcon}
        </span>
      ) : null}
    </AutocompletePrimitive.Item>
  )
}

type CommandEmptyProps = React.ComponentProps<"div">

function CommandEmpty({ className, ...props }: CommandEmptyProps) {
  return (
    <div
      data-slot="command-empty"
      className={cn(
        "px-[var(--hui-space-3)] py-[var(--hui-space-7)] text-center text-[var(--hui-color-foreground-base-secondary)] [font-size:var(--hui-font-size-control,13px)] [line-height:var(--hui-line-height-control,16px)]",
        className
      )}
      {...props}
    />
  )
}

type CommandGroupProps = AutocompletePrimitive.Group.Props

function CommandGroup({ children, className, ...props }: CommandGroupProps) {
  const { inputValue, usesItemsProp } = useCommandContext()

  if (!usesItemsProp && inputValue) return <>{children}</>

  return (
    <AutocompletePrimitive.Group
      data-slot="command-group"
      className={cn(
        "flex flex-col gap-[var(--hui-space-1)] py-[var(--hui-space-3)]",
        className
      )}
      {...props}
    >
      {children}
    </AutocompletePrimitive.Group>
  )
}

type CommandLabelProps = AutocompletePrimitive.GroupLabel.Props

function CommandLabel({ className, ...props }: CommandLabelProps) {
  const { inputValue, usesItemsProp } = useCommandContext()

  if (!usesItemsProp && inputValue) return null

  return (
    <AutocompletePrimitive.GroupLabel
      data-slot="command-label"
      className={cn(
        "flex items-center px-[var(--hui-space-3)] py-[var(--hui-space-2)] text-[var(--hui-color-foreground-base-secondary)] [font-size:var(--hui-font-size-micro)] [font-weight:var(--hui-font-weight-regular)] [letter-spacing:var(--hui-letter-spacing-micro)] [line-height:var(--hui-line-height-micro)]",
        className
      )}
      {...props}
    />
  )
}

type CommandSeparatorProps = AutocompletePrimitive.Separator.Props

function CommandSeparator({ className, ...props }: CommandSeparatorProps) {
  const { inputValue, usesItemsProp } = useCommandContext()

  if (!usesItemsProp && inputValue) return null

  return (
    <AutocompletePrimitive.Separator
      data-slot="command-separator"
      className={cn(
        "my-[var(--hui-space-1)] h-px bg-[var(--hui-color-border-base-primary)]",
        className
      )}
      {...props}
    />
  )
}

function CommandDialog(props: DialogPrimitive.Root.Props) {
  return <DialogPrimitive.Root {...props} />
}

function CommandDialogTrigger(props: DialogPrimitive.Trigger.Props) {
  return (
    <DialogPrimitive.Trigger data-slot="command-dialog-trigger" {...props} />
  )
}

type CommandDialogContentProps = DialogPrimitive.Popup.Props & {
  title?: string
  width?: React.CSSProperties["width"]
}

function CommandDialogContent({
  className,
  children,
  title = "Command palette",
  width,
  style,
  ...props
}: CommandDialogContentProps) {
  return (
    <DialogPrimitive.Portal>
      <DialogPrimitive.Viewport
        data-slot="command-dialog-viewport"
        className="pointer-events-none fixed inset-0 z-[var(--hui-z-index-portal)]"
      >
        <DialogPrimitive.Popup
          data-slot="command-dialog-content"
          className={cn(
            "pointer-events-auto fixed top-0 left-1/2 isolate flex max-h-[min(420px,80vh)] min-h-0 w-[540px] max-w-[90vw] flex-col overflow-hidden rounded-[var(--hui-radius-2)] bg-[var(--hui-color-background-base-primary)] shadow-[var(--hui-shadow-floating)] [transform:translate(-50%,min(160px,calc(50vh-50%)))] outline-none motion-safe:[transition:opacity_var(--hui-duration-fast)_var(--hui-ease-out)] motion-safe:data-ending-style:opacity-0 motion-safe:data-starting-style:opacity-0",
            className
          )}
          style={{ width, ...style }}
          {...props}
        >
          <DialogPrimitive.Title className="sr-only">
            {title}
          </DialogPrimitive.Title>
          {children}
        </DialogPrimitive.Popup>
      </DialogPrimitive.Viewport>
    </DialogPrimitive.Portal>
  )
}

export {
  Command,
  CommandContent,
  CommandDialog,
  CommandDialogContent,
  CommandDialogTrigger,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandLabel,
  CommandSeparator,
}

export type {
  CommandContentProps,
  CommandDialogContentProps,
  CommandEmptyProps,
  CommandGroupProps,
  CommandInputProps,
  CommandItemProps,
  CommandLabelProps,
  CommandProps,
  CommandSeparatorProps,
}

```

      
        Update the import paths to match your project setup.
      
    
  


## Usage [#usage]

```tsx
import {
  Command,
  CommandContent,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandLabel,
} from "@/components/ui/command";
```

```tsx
<Command>
  <CommandInput placeholder="Search commands..." />
  <CommandContent>
    <CommandEmpty>No matching commands.</CommandEmpty>
    <CommandGroup>
      <CommandLabel>Workspace</CommandLabel>
      <CommandItem value="new document">New document</CommandItem>
      <CommandItem value="workspace settings">Workspace settings</CommandItem>
    </CommandGroup>
  </CommandContent>
</Command>
```

Always include `CommandEmpty`. It renders only when filtering leaves no results, so there is no cost to having it and no excuse to omit it.

## Don't do this [#dont-do-this]

### No guidance when nothing matches [#no-guidance-when-nothing-matches]

```tsx
// Bad
<CommandContent>
  {items.map((item) => (
    <CommandItem key={item.id}>{item.label}</CommandItem>
  ))}
</CommandContent>
```

```tsx
// Good
<CommandContent>
  <CommandEmpty>
    No matches for “{query}”. Try a different term or browse all commands.
  </CommandEmpty>
  {items.map((item) => (
    <CommandItem key={item.id}>{item.label}</CommandItem>
  ))}
</CommandContent>
```

A blank panel after a typo looks like the app froze, and people cannot tell whether their query matched nothing or the list failed to load. State what happened and what to try next — the empty string is part of the interface, not an edge case.

### Destructive actions with no guardrail [#destructive-actions-with-no-guardrail]

```tsx
// Bad
<CommandItem onClick={deleteWorkspace}>
  Delete workspace
</CommandItem>
```

```tsx
// Good
<CommandItem onClick={() => setConfirmOpen(true)}>
  Delete workspace…
</CommandItem>
```

A palette makes destructive actions dangerously easy: one typed prefix, one press of <kbd>Enter</kbd>. Route irreversible commands through a confirmation step instead of executing immediately. The ellipsis in the label is a small cue that more will be asked before anything happens.

### A palette nobody can find [#a-palette-nobody-can-find]

```tsx
// Bad
useEffect(() => {
  // ⌘K opens the palette — nothing rendered anywhere
}, [])
```

```tsx
// Good
<CommandDialogTrigger render={<Button variant="secondary" />}>
  Search actions <span className="text-xs opacity-72">⌘K</span>
</CommandDialogTrigger>
```

Shortcuts are invisible until discovered. If the trigger exists nowhere on screen, people who never read your docs — which is most people — never learn the feature exists. Render a visible trigger with the shortcut hint next to it and teach the shortcut by exposure.

## Examples [#examples]

### Inline command list [#inline-command-list]

Grouping, filtering, keyboard hints in the trailing slot, icons, and visible action feedback announced through a polite live region.

```tsx
"use client"

import * as React from "react"
import {
  File as FileIcon,
  Palette as PaletteIcon,
  Search as SearchIcon,
  Settings as SettingsIcon,
  User as UserIcon,
} from "honestui/icons"

import {
  Command,
  CommandContent,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandLabel,
  CommandSeparator,
} from "@/components/honest-ui/ui/command"

export function CommandDemo() {
  const [lastAction, setLastAction] = React.useState("No command selected")

  return (
    <div className="w-full max-w-lg space-y-3">
      <Command className="rounded-sm border-[0.5px] border-border shadow-sm">
        <CommandInput
          leadingIcon={<SearchIcon />}
          placeholder="Search commands..."
        />
        <CommandContent>
          <CommandEmpty>No matching commands.</CommandEmpty>
          <CommandGroup>
            <CommandLabel>Navigation</CommandLabel>
            <CommandItem
              leadingIcon={<FileIcon />}
              trailingIcon={<span className="text-xs">G then D</span>}
              onClick={() => setLastAction("Opened documents")}
            >
              Open documents
            </CommandItem>
            <CommandItem
              leadingIcon={<UserIcon />}
              onClick={() => setLastAction("Opened account")}
            >
              Open account
            </CommandItem>
          </CommandGroup>
          <CommandSeparator />
          <CommandGroup>
            <CommandLabel>Preferences</CommandLabel>
            <CommandItem
              leadingIcon={<PaletteIcon />}
              onClick={() => setLastAction("Opened appearance")}
            >
              Change appearance
            </CommandItem>
            <CommandItem
              leadingIcon={<SettingsIcon />}
              onClick={() => setLastAction("Opened settings")}
            >
              Open settings
            </CommandItem>
          </CommandGroup>
        </CommandContent>
      </Command>
      <p className="text-center text-xs text-muted-foreground" aria-live="polite">
        {lastAction}
      </p>
    </div>
  )
}

```

### Command dialog [#command-dialog]

A controlled palette bound to <kbd>Ctrl</kbd>/<kbd>Cmd</kbd>+<kbd>K</kbd>, with a visible trigger, a scoped hidden title, and closing after each command runs.

```tsx
"use client"

import * as React from "react"
import {
  Command as CommandIcon,
  File as FileIcon,
  Search as SearchIcon,
  Settings as SettingsIcon,
  User as UserIcon,
} from "honestui/icons"

import { Button } from "@/components/honest-ui/ui/button"
import {
  Command,
  CommandContent,
  CommandDialog,
  CommandDialogContent,
  CommandDialogTrigger,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandLabel,
} from "@/components/honest-ui/ui/command"

export function CommandDialogDemo() {
  const [open, setOpen] = React.useState(false)
  const [lastAction, setLastAction] = React.useState("No command selected")

  React.useEffect(() => {
    function handleKeyDown(event: KeyboardEvent) {
      if (event.key.toLocaleLowerCase() === "k" && (event.metaKey || event.ctrlKey)) {
        event.preventDefault()
        setOpen((currentOpen) => !currentOpen)
      }
    }

    document.addEventListener("keydown", handleKeyDown)
    return () => document.removeEventListener("keydown", handleKeyDown)
  }, [])

  function runCommand(message: string) {
    setLastAction(message)
    setOpen(false)
  }

  return (
    <div className="flex flex-col items-center gap-3">
      <CommandDialog open={open} onOpenChange={setOpen}>
        <CommandDialogTrigger render={<Button variant="secondary" />}>
          <CommandIcon />
          Open command palette
          <span className="text-xs opacity-72">⌘K</span>
        </CommandDialogTrigger>
        <CommandDialogContent title="Workspace commands">
          <Command>
            <CommandInput
              leadingIcon={<SearchIcon />}
              placeholder="Search workspace commands..."
            />
            <CommandContent>
              <CommandEmpty>No matching commands.</CommandEmpty>
              <CommandGroup>
                <CommandLabel>Workspace</CommandLabel>
                <CommandItem
                  leadingIcon={<FileIcon />}
                  onClick={() => runCommand("Opened a new document")}
                >
                  New document
                </CommandItem>
                <CommandItem
                  leadingIcon={<UserIcon />}
                  onClick={() => runCommand("Opened team members")}
                >
                  View team members
                </CommandItem>
                <CommandItem
                  leadingIcon={<SettingsIcon />}
                  onClick={() => runCommand("Opened workspace settings")}
                >
                  Workspace settings
                </CommandItem>
              </CommandGroup>
            </CommandContent>
          </Command>
        </CommandDialogContent>
      </CommandDialog>
      <p className="text-xs text-muted-foreground" aria-live="polite">
        {lastAction}
      </p>
    </div>
  )
}

```

## API reference [#api-reference]

### `Command` [#command]

Forwards Base UI Autocomplete root props plus:

| Prop                                       | Values                   | Default      |
| ------------------------------------------ | ------------------------ | ------------ |
| `items`                                    | readonly array           | —            |
| `value` / `defaultValue` / `onValueChange` | string                   | uncontrolled |
| `open`                                     | boolean                  | `true`       |
| `inline`                                   | boolean                  | `true`       |
| `autoHighlight`                            | `"always"`, `"never"`, … | `"always"`   |
| `keepHighlight`                            | boolean                  | `true`       |

Passing `items` switches filtering from Honest UI's JSX fallback to Base UI's own item filtering.

### `CommandInput` [#commandinput]

Accepts Base UI Autocomplete input props plus:

| Prop          | Values                | Default             |
| ------------- | --------------------- | ------------------- |
| `leadingIcon` | ReactNode             | —                   |
| `size`        | `sm`, `default`, `lg` | `lg`                |
| `placeholder` | string                | `"Search..."`       |
| `autoFocus`   | boolean               | `true`              |
| `aria-label`  | string                | `"Search commands"` |

### `CommandItem` [#commanditem]

Accepts Base UI item props plus `leadingIcon` and `trailingIcon`. Without an explicit `value`, a plain-string child becomes the filterable value. `onClick` runs on <kbd>Enter</kbd> and on click.

### Other parts [#other-parts]

`CommandContent`, `CommandEmpty`, `CommandGroup`, `CommandLabel`, and `CommandSeparator` accept their matching Base UI element props. During JSX filtering, groups, labels, and separators hide themselves once a query is active.

### Dialog parts [#dialog-parts]

`CommandDialog` and `CommandDialogTrigger` forward Base UI Dialog root and trigger props. `CommandDialogContent` forwards popup props plus:

| Prop    | Values    | Default             |
| ------- | --------- | ------------------- |
| `title` | string    | `"Command palette"` |
| `width` | CSS width | `540px`, max `90vw` |

See the [Base UI Autocomplete API](https://base-ui.com/react/components/autocomplete#api-reference) and [Base UI Dialog API](https://base-ui.com/react/components/dialog#api-reference) for controlled state, item collections, filtering, and focus behavior.
