Skip to the content
BooleanPress UI

Menu

Command

A search field above a filtered list of commands or pages, inline or in a dialog.

Import

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

Also install cmdk: pnpm add cmdk

Usage

Command is cmdk with the package's look. Typing filters the items; the first match is highlighted and Enter runs its onSelect. Name the search field with cmdk's label prop on Command.

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

export function Pages() {
  return (
    <Command label="Search pages">
      <CommandInput placeholder="Search for a page" />
      <CommandList>
        <CommandEmpty>No results.</CommandEmpty>
        <CommandGroup heading="Pages">
          <CommandItem onSelect={() => open("/logs")}>Email log</CommandItem>
          <CommandItem onSelect={() => open("/mailers")}>Mailers</CommandItem>
        </CommandGroup>
      </CommandList>
    </Command>
  )
}

CommandDialog puts the same parts in a dialog; control it with open and onOpenChange, and open it from a button and a shortcut of your own. Its hidden title and description are inside the dialog and come from the provider's commandTitle and commandDescription; pass title and description to override them. The dialog's title also names the search field. Give CommandItem a value when its text is not what people should search, keywords for extra search words, and disabled to skip it. CommandShortcut shows a key hint; it does not bind the key. cmdk filters by itself: pass shouldFilter={false} to Command and filter the items yourself for server-side search.

Examples

Basic

An inline list with two groups and shortcut hints.

In a dialog

CommandDialog, opened by the button or by ⌘J / Ctrl+J.

Empty result

CommandEmpty shows when nothing matches the search.

Disabled item

A disabled item is skipped by the arrow keys and cannot be chosen.

Accessibility

Semantics
The search field is an input with role="combobox", aria-expanded, aria-controls and aria-activedescendant; the list is a role="listbox" named from the provider's suggestions string, and each item a role="option" with aria-selected. Groups are role="group" named by their heading. Focus stays in the field while the arrow keys move the highlight.
Labels
Name an inline command with the label prop on Command; it names the search field. CommandDialog names it with the dialog's title. Translate the list's name, the dialog title and the dialog description in the provider.
Focus
Focus goes to the search field when a CommandDialog opens and returns to what opened it on close. Inline, the field is a normal tab stop; the list is never focused.
Known limits
  • cmdk also binds Ctrl+N, Ctrl+J (next) and Ctrl+P, Ctrl+K (previous) while the field has focus. Pass vimBindings={false} to Command if your shortcuts collide.
  • While nothing matches, cmdk leaves an empty listbox with the CommandEmpty message beside it; axe reports an empty listbox as aria-required-children. The message is still read as text.
  • Do not put CommandSeparator inside the list: cmdk renders it as role="separator" in a listbox, which axe reports as aria-required-children. Separate groups by their headings.
  • CommandShortcut is only a hint. Bind the key yourself, and not on a key that cmdk uses.

Keyboard

Keyboard
KeyBehaviour
↓Highlights the next item, wrapping from the last to the first (loop).
↑Highlights the previous item.
HomeHighlights the first item.
EndHighlights the last item.
EnterRuns the highlighted item's onSelect.
EscapeIn a CommandDialog, closes it and returns focus.

API

Command

Renders cmdk Command and passes it every other prop.

Command props
PropTypeDefaultDescription
asChildboolean
disablePointerSelectionbooleanOptionally set to true to disable selection via pointer events.
filterCommandFilterCustom filter function for whether each command menu item should matches the given search query. It should return a number between 0 and 1, with 1 being the best match and 0 being hidden entirely. By default, uses the command-score library.
labelstringAccessible label for this command menu. Not shown visibly.
loopbooleanOptionally set to true to turn on looping around when using the arrow keys.
onValueChange((value: string) => void)Event handler called when the selected item of the menu changes.
shouldFilterbooleanOptionally set to false to turn off the automatic filtering and sorting. If false, you must conditionally render valid items based on the search query yourself.
valuestringOptional controlled state of the selected command menu item.
vimBindingsbooleanSet to false to disable ctrl+n/j/p/k shortcuts. Defaults to true.

CommandDialog

CommandDialog props
PropTypeDefaultDescription
classNamestring
defaultOpenboolean
descriptionstringThe hidden dialog description. Defaults to the provider's commandDescription.
modalboolean
onOpenChange((open: boolean) => void)Called with true or false when it opens or closes.
openbooleanWhether it is open. Pair it with onOpenChange.
showCloseButtonbooleantrueRender the × button. true by default.
titlestringThe hidden dialog title, which also names the search field. Defaults to the provider's commandTitle.

CommandInput

Renders cmdk Command.Input and passes it every other prop.

CommandInput props
PropTypeDefaultDescription
asChildboolean
onValueChange((search: string) => void)Event handler called when the search value changes.
valuestringOptional controlled state for the value of the search input.

CommandList

Renders cmdk Command.List and passes it every other prop.

CommandList props
PropTypeDefaultDescription
asChildboolean
labelstringAccessible label for this List of suggestions. Not shown visibly.

CommandEmpty

Renders cmdk Command.Empty and passes it every other prop.

CommandEmpty props
PropTypeDefaultDescription
asChildboolean

CommandGroup

Renders cmdk Command.Group and passes it every other prop.

CommandGroup props
PropTypeDefaultDescription
asChildboolean
forceMountbooleanWhether this group is forcibly rendered regardless of filtering.
headingReactNodeOptional heading to render for this group.
valuestringIf no heading is provided, you must provide a value that is unique for this group.

CommandItem

Renders cmdk Command.Item and passes it every other prop.

CommandItem props
PropTypeDefaultDescription
asChildboolean
disabledbooleanWhether this item is currently disabled.
forceMountbooleanWhether this item is forcibly rendered regardless of filtering.
keywordsstring[]Optional keywords to match against when filtering.
onSelect((value: string) => void)Event handler for when this item is selected, either via click or keyboard selection.
valuestringA unique value for this item. If no value is provided, it will be inferred from children or the rendered textContent. If your textContent changes between renders, you must provide a stable, unique value.

CommandShortcut

Renders a span and passes it every other prop.

CommandSeparator

Renders cmdk Command.Separator and passes it every other prop.

CommandSeparator props
PropTypeDefaultDescription
alwaysRenderbooleanWhether this separator should always be rendered. Useful if you disable automatic filtering.
asChildboolean

Every part takes className, merged with its defaults by cn(), and ref, which reaches the element it renders.

Data attributes: data-slot="command" (Command), data-slot="command-input-wrapper" (CommandInput), data-slot="command-list" (CommandList), data-slot="command-empty" (CommandEmpty), data-slot="command-group" (CommandGroup), data-slot="command-item" (CommandItem), data-slot="command-shortcut" (CommandShortcut), data-slot="command-separator" (CommandSeparator).

Provider strings: commandTitle, commandDescription, suggestions (BooleanUIProvider's strings).

Theming

The list is a floating surface: --popover and --popover-foreground. The highlighted item uses --accent and --accent-foreground.

The tokens its classes read. Change their values in your theme, and it follows.

Theme tokens
TokenUsed for
--accentbackground
--accent-foregroundtext
--borderbackground
--foregroundtext
--muted-foregroundtext
--popoverbackground
--popover-foregroundtext