Command
Jumps to records, pages and commands from the keyboard, filtering as you type. For a form field, use Select or Combobox.
Installation
The CLI installs the Radix or Base UI file to match your project, with what it builds on: dialog, input-group.
If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.
When to use
Use it
- People search a mixed list of things to do or places to go, like recent records, actions and app pages, and pick one from the keyboard.
- A command palette: a search box and its results in a dialog that opens from a button or a shortcut.
- A choice made from a button that people search, like switching a list view from a popover. The choice acts at once and isn't kept as a form field's value.
- A list that stays on the page with a search field above it, like a quick find in a side panel.
Don't use it
- The value belongs to a form field, even one shown in a popover. Use Select for short lists or Combobox for long ones, which keeps the value in the field.
- People pick Salesforce records with object icons and a meta line. Use Lookup.
- The list holds a few actions on one thing, opened from a button. Use Dropdown menu.
Usage
Command
├── CommandInput
└── CommandList
├── CommandEmpty
├── CommandGroup
│ └── CommandItem
│ └── CommandShortcut
└── CommandSeparatorGive Command a label, which names the search field.
import {
Command,
CommandEmpty,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command"<Command label="Search Salesforce">
<CommandInput placeholder="Search records..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandItem value="Rose Gonzalez" onSelect={openRecord}>
Rose Gonzalez
</CommandItem>
</CommandList>
</Command>onSelect gets the item's value, from Enter or a click. Items filter on their text, shortcut included, so pass value with the words people search for and keywords for other words that should match. For a palette, put a Command inside CommandDialog, as the In a dialog example shows; it has no close button unless you pass showCloseButton. For a choice from a button, put it inside PopoverContent with p-0, as the Selected and disabled example shows.
Examples
In a dialog
The palette in `CommandDialog`: a Search button opens it, and picking a record or app page closes it.
Search Salesforce
Search recent records or go to an app page.
Selected and disabled
A list view switcher in `PopoverContent`, the current view checked with `aria-checked` and one view `disabled`.
No results
A search that matches nothing shows the `CommandEmpty` row.
API reference
| Part | Built on |
|---|---|
Command | cmdk Command (opens in a new tab) |
CommandDialog | Dialog |
CommandInput | cmdk Command.Input (opens in a new tab) |
CommandList | cmdk Command.List (opens in a new tab) |
CommandEmpty | cmdk Command.Empty (opens in a new tab) |
CommandGroup | cmdk Command.Group (opens in a new tab) |
CommandSeparator | cmdk Command.Separator (opens in a new tab) |
CommandItem | cmdk Command.Item (opens in a new tab) |
CommandShortcut | <span> (opens in a new tab) |
CommandDialog
| Prop | Type | Default |
|---|---|---|
title | string | "Command Palette" |
description | string | "Search for a command to run..." |
showCloseButton | boolean | false |
SLDS mapping
SLDS has no command palette, so Command is built from the parts it does have. It runs on cmdk in Radix and Base UI projects alike; cmdk depends on @radix-ui/react-dialog, so a Base UI project installs it and the Radix packages it needs too.
Commandisslds-dropdownwithout its shadow. InsideCommandDialogit takes the modal's corners.CommandInputis input group's search field,slds-input-has-icon_left.CommandListisslds-listbox_verticalatslds-dropdown_length-7.CommandItemis a plain listbox option,slds-listbox__option_plain, with SLDS's icon column. The check is SLDS's selected option check,slds-is-selected.CommandGroup's heading isslds-listbox__option-header, andCommandSeparatorisslds-has-divider_top-space.CommandEmptyis a plain option row in muted text, since SLDS has no "no results" row.CommandDialogis aslds-modalwith no header, footer or padding.
Known gaps:
- SLDS centers its modal.
CommandDialogopens a third of the way down, so it doesn't move as the results change. - An item with an icon starts its text where SLDS's listbox does, further in than a Dropdown menu item, which follows SLDS's menu. A command used as a menu won't line up with one.
- No entity options with a meta line and object icons. Use Lookup for records.
| Prop | Value | SLDS | Notes |
|---|---|---|---|
Command | default | slds-dropdown (surface, no shadow), slds-listbox_vertical | No SLDS counterpart. Popover fill, radius-2 corners, 12px text. Add a shadow through className when it stands alone. |
CommandInput | default | slds-input-has-icon_left, slds-input__icon_left | Input group's search field, inset 12px, with the input focus ring. |
CommandItem | default | slds-listbox__option_plain slds-media_small | 34px rows. Active: menu fill, and SLDS's inset ring while focus is inside. |
CommandItem data-checked / aria-checked | true | slds-listbox__option.slds-is-selected | The check in SLDS's 24px icon column, before the text. On an item with its own icon, the check goes at the end, as shadcn's does. |
CommandGroup heading | default | slds-listbox__option-header | |
CommandSeparator | default | slds-has-divider_top-space | |
CommandDialog | default | slds-modal (headless, footless) | A third of the way down the screen, as shadcn places it. |
Keyboard interactions
| Keys | Action |
|---|---|
| Typing | Filters the items. The first match becomes active. |
| Down Arrow, Up Arrow | Shows the active item, then moves to the next or previous one, skipping disabled ones. |
| Home, End, Cmd+Up Arrow, Cmd+Down Arrow | Moves to the first or last item. |
| Alt+Down Arrow, Alt+Up Arrow | Moves to the first item of the next or previous group. |
| Ctrl+N, Ctrl+J / Ctrl+P, Ctrl+K | Moves down or up, as the arrows do. Turn off with vimBindings={false}. |
| Enter | Selects the active item, shown or not, so typing then Enter runs the first match. |
| Escape | In a CommandDialog or popover, closes it. Inline, does nothing. |
A palette's shortcut, like Cmd+K, is yours to add.
Accessibility
- Always pass
labeltoCommand. Inside a popover, also givePopoverContentanaria-label. CommandDialog'stitleanddescriptionname the dialog for screen readers and aren't shown. Set them to what the palette searches.- Mark the current choice with
aria-checkedon every item:trueon the chosen one,falseon the rest, so their text lines up. Screen reader support for it on an option varies, so also show the choice on the button that opens the list. - Screen readers aren't told how many items match, or that nothing does. Keep a
role="status"element outside the list and put the count or "No results" in it. An axe scan also flags the empty row inside the list. CommandDialogreturns focus on close to whatever had it when it opened, as long as that element is still on the page and can take focus.
Motion
CommandDialog's backdrop and box fade in and out as Dialog's do. Fades don't move anything, so they stay on with reduced motion.
Responsive behavior
Commandfills its container's width. Give it amax-w-*on a wide page.- The list shows seven rows, then scrolls. Long item text wraps, and shortcuts stay at the end of the row.
CommandDialogis half the screen wide, between 20rem and 40rem, or the screen less 2rem a side on small screens. It stops 1rem above the bottom, and on a short screen its list scrolls inside it.
Performance
- Every item renders and is scored on every keystroke. A few hundred items filter without trouble.
- For thousands, or for results from a server, pass
shouldFilter={false}toCommand, controlCommandInput'svalue, and render only the items you fetched.
Rules
CommandDialogdoesn't add aCommand. Put one inside it.- Use
CommandGroup'sheadingfor headers, not your own elements, and aCommandSeparatorbetween groups. - Don't add your own check icon.
aria-checkedordata-checkedon an item draws the check. - Inside a popover or dialog, add no shadow; they have their own. On its own on a page, give
Commandshadow-(--elevation-2), as the Search records and actions example does. - Keep a list to seven rows or fewer, headers included, before people have to scroll.
Related
- SelectPicks one value from a short list in a dropdown, as a picklist does.
- ComboboxFilters a list of options as you type, for one value or several shown as pills.
- LookupPicks records from a search, one in the field or several as pills.
- Dropdown menuOpens a menu of actions or options from a button, like a record's row actions.