Skip to content
UI

Command

A searchable list of pages and actions, inline or as a command palette in a dialog.

@zenoxai/ui/components/command Markdown
<Command className="max-w-sm rounded-lg border border-border">
  <CommandInput placeholder="Search pages and actions" />
  <CommandList>
    <CommandEmpty>No results.</CommandEmpty>
    <CommandGroup heading="Pages">
      <CommandItem>
        <BellIcon />
        Alerts
      </CommandItem>
      <CommandItem>
        <ServerIcon />
        Assets
      </CommandItem>
      <CommandItem>
        <ShieldAlertIcon />
        Vulnerabilities
      </CommandItem>
      <CommandItem>
        <FileTextIcon />
        Reports
      </CommandItem>
    </CommandGroup>
    <CommandSeparator />
    <CommandGroup heading="Actions">
      <CommandItem>
        <PlusIcon />
        New investigation
        <CommandShortcut>⌘I</CommandShortcut>
      </CommandItem>
      <CommandItem>
        <FileTextIcon />
        Export weekly report
        <CommandShortcut>⌘E</CommandShortcut>
      </CommandItem>
    </CommandGroup>
  </CommandList>
</Command>

Usage

import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@zenoxai/ui/components/command"
<Command>
  <CommandInput placeholder="Search pages and actions" />
  <CommandList>
    <CommandEmpty>No results.</CommandEmpty>
    <CommandGroup heading="Pages">
      <CommandItem onSelect={() => navigate("/alerts")}>Alerts</CommandItem>
      <CommandItem onSelect={() => navigate("/assets")}>Assets</CommandItem>
    </CommandGroup>
  </CommandList>
</Command>

Command is built on cmdk. It filters and ranks its items as the user types, moves the highlight with the arrow keys and runs onSelect on Enter or click. Use it for long lists of commands or destinations that people find faster by typing than by scanning: a command palette, a “go to” list, a picker with dozens of entries.

  • For a few actions behind a button, use a Dropdown Menu.
  • For a value in a form, use a Combobox: it has the field semantics a form needs.

Items match on their text. When the text is not what people type, or two items share it, pass a value (and keywords for synonyms). CommandEmpty shows when nothing matches. CommandShortcut only displays a shortcut; bind the key yourself.

When it mounts, Command scrolls its first item into view, and that scrolls the page too. Place an inline Command where it is visible on load, or inside an overlay that mounts it on open, as in the examples below.

Examples

Command palette

CommandDialog puts a Command in a Dialog, placed in the upper third of the screen. It has no trigger part: control open and open it from a button, a keyboard shortcut, or both. Close it in each item’s onSelect. title and description label the dialog for screen readers and are not shown.

function CommandInDialog() {
  const [open, setOpen] = React.useState(false)

  return (
    <>
      <Button variant="outline" onClick={() => setOpen(true)}>
        <SearchIcon data-icon="inline-start" />
        Search
      </Button>
      <CommandDialog
        open={open}
        onOpenChange={setOpen}
        title="Command palette"
        description="Search for a page or an action"
      >
        <Command>
          {/* Each item's onSelect navigates or runs its action, then closes. */}
          <CommandInput placeholder="Type a command or search" />
          <CommandList>
            <CommandEmpty>No results.</CommandEmpty>
            <CommandGroup heading="Pages">
              <CommandItem onSelect={() => setOpen(false)}>
                <BellIcon />
                Alerts
              </CommandItem>
              <CommandItem onSelect={() => setOpen(false)}>
                <ServerIcon />
                Assets
              </CommandItem>
              <CommandItem onSelect={() => setOpen(false)}>
                <FileTextIcon />
                Reports
              </CommandItem>
            </CommandGroup>
            <CommandSeparator />
            <CommandGroup heading="Actions">
              <CommandItem onSelect={() => setOpen(false)}>
                <PlusIcon />
                New investigation
              </CommandItem>
            </CommandGroup>
          </CommandList>
        </Command>
      </CommandDialog>
    </>
  )
}

In a popover

For a picker attached to a button, such as the assignee of an alert, put a Command in a Popover with p-0 on the content. Set data-checked on the current item to show a check mark at its end, and close the popover in onSelect. When the value belongs to a form field, use a Combobox instead.

function CommandInPopover() {
  const [open, setOpen] = React.useState(false)
  const [assignee, setAssignee] = React.useState("Marina Santos")
  const analysts = ["Marina Santos", "Jordan Lee", "Ada Okafor", "Thanh Nguyen"]

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger render={<Button variant="outline" />}>
        <UserIcon data-icon="inline-start" />
        {assignee}
      </PopoverTrigger>
      <PopoverContent align="start" className="w-60 p-0">
        <Command>
          <CommandInput placeholder="Assign to" />
          <CommandList>
            <CommandEmpty>No analyst found.</CommandEmpty>
            <CommandGroup heading="Analysts">
              {analysts.map((name) => (
                <CommandItem
                  key={name}
                  data-checked={assignee === name}
                  onSelect={() => {
                    setAssignee(name)
                    setOpen(false)
                  }}
                >
                  {name}
                </CommandItem>
              ))}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  )
}

Accessibility

  • The input is a combobox that controls the list. Arrow keys move the highlight, Enter selects and the highlighted item is announced; focus stays in the input.
  • Give CommandDialog a title that names its purpose (“Command palette”, “Go to page”). It is the dialog’s accessible name.
  • Group headings are announced as the names of their groups.

API reference

Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.

Command

Command is a searchable list of actions, built on cmdk: type to filter, arrows to move, Enter to run. Use it for a command palette (CommandDialog) or an action picker in a popover. To pick a form value from a long list, use Combobox.

No props of its own. Accepts the props of the element it renders.

CommandDialog

CommandDialog opens a Command in a modal dialog near the top of the screen: the command palette. Control it with `open` and `onOpenChange`, and bind the shortcut that opens it yourself.

PropTypeDefault
actionsRefRefObject<DialogRootActions | null>

A ref to imperative actions. - `unmount`: Ends the closing phase of the dialog after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the dialog completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the dialog imperatively when called.

-
defaultOpenboolean

Whether the dialog is initially open.

false
defaultTriggerIdstring | null

ID of the trigger that the dialog is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open dialog.

-
descriptionstring

Accessible description, visually hidden; pass a translated one.

Search for a command to run
disablePointerDismissalboolean

Whether to prevent the dialog from closing on outside presses. For non-modal dialogs, this also prevents the dialog from closing when focus moves outside of it.

false
handleDialogHandle<unknown>

A handle to associate the dialog with a trigger. If specified, allows external triggers to control the dialog's open state. Can be created with the Dialog.createHandle() method.

-
modalboolean | "trap-focus"

Determines if the dialog enters a modal state when open. - `true`: user interaction is limited to just the dialog: focus is trapped, document page scroll is locked, and pointer interactions on outside elements are disabled. - `false`: user interaction with the rest of the document is allowed. - `'trap-focus'`: focus is trapped inside the dialog, but document page scroll is not locked and pointer interactions outside of it remain enabled.

true
onOpenChange((open: boolean, eventDetails: DialogRootChangeEventDetails) => void)

Event handler called when the dialog is opened or closed.

-
onOpenChangeComplete((open: boolean) => void)

Event handler called after any animations complete when the dialog is opened or closed.

-
openboolean

Whether the dialog is currently open.

-
showCloseButtonboolean

Shows the X button at the top-right corner; Escape closes it anyway.

false
titlestring

Accessible name of the dialog, visually hidden; pass a translated one.

Command palette
triggerIdstring | null

ID of the trigger that the dialog is associated with. This is useful in conjunction with the `open` prop to create a controlled dialog. There's no need to specify this prop when the dialog is uncontrolled (that is, when the `open` prop is not set).

-

CommandInput

CommandInput is the search field that filters the items.

No props of its own. Accepts the props of the element it renders.

CommandList

No props of its own. Accepts the props of the element it renders.

CommandEmpty

CommandEmpty is the message shown when no item matches the query.

No props of its own. Accepts the props of the element it renders.

CommandGroup

No props of its own. Accepts the props of the element it renders.

CommandItem

No props of its own. Accepts the props of the element it renders.

CommandShortcut

CommandShortcut is the key hint at the right edge of an item. It is display text only: bind the keys yourself.

No props of its own. Accepts the props of the element it renders.

CommandSeparator

No props of its own. Accepts the props of the element it renders.