Command Primitive
Command provides an accessible action-result primitive with label-only selection queries and separate keyword matching while product workflows remain consumer-owned.
Overview and decision boundary
Use Command for an accessible local action picker whose query, results, and selection events remain composable and consumer-owned.
Do not add hidden fuzzy ranking, remote fetching, route navigation, or global shortcut registration to Command.
Selected value: None
import { Kbd } from "@nerio-ui/ui";import { Command, CommandEmpty, CommandInput, CommandItem, CommandList } from "@nerio-ui/ui/client";const items = [ { value: "settings", label: "Workspace settings", keywords: ["preferences"] }, { value: "archive", label: "Archive workspace", disabled: true },];<Command items={items}> <CommandInput aria-label="Workspace commands" placeholder="Search commands" /> <CommandEmpty>No matching commands.</CommandEmpty> <CommandList> {(item) => ( <CommandItem key={item.value} value={item.value} disabled={item.disabled} shortcut={<Kbd aria-hidden>⌘ ,</Kbd>} onSelect={(value, event) => runCommand(value, event)} > {item.label} </CommandItem> )} </CommandList></Command>Installation and imports
Install the editable registry source, or use the matching package entrypoint when the product keeps Nerio as a workspace dependency.
pnpm dlx nerio add command-primitiveimport { Kbd } from '@nerio-ui/ui';import { Command, CommandEmpty, CommandInput, CommandItem, CommandList } from '@nerio-ui/ui/client';Usage
<Command items={items} filter={false} query={query} onQueryChange={setQuery}>...</Command><Popover trigger="Open commands"> <Command>...</Command></Popover><Dialog trigger="Open dialog" title="Workspace commands"> <Command>...</Command></Dialog><Sheet> <SheetTrigger render={<Button>Open sheet</Button>} /> <SheetContent><Command>...</Command></SheetContent></Sheet>import { Kbd } from '@nerio-ui/ui';import { Command, CommandEmpty, CommandInput, CommandItem, CommandList } from '@nerio-ui/ui/client';const items = [{ value: "settings", label: "Workspace settings", keywords: ["preferences"] }];<Command items={items}> <CommandInput aria-label="Workspace commands" placeholder="Search commands" /> <CommandEmpty>No matching commands.</CommandEmpty> <CommandList> {(item) => ( <CommandItem key={item.value} value={item.value} shortcut={<Kbd aria-hidden>⌘,</Kbd>} onSelect={(value, event) => runCommand(value, event)}> {item.label} </CommandItem> )} </CommandList></Command>Variants
Selected value: None
<Command items={groupedItems}>...</Command>| Mode | Contract |
|---|---|
Local | Locale-aware matching uses label, value, and keywords; selection writes only the visible label. |
Consumer-filtered | filter={false}; consumers replace items during loading or remote work. |
Grouped | Labelled group records preserve stable leaf values and listbox semantics. |
Overlay | The same inline primitive composes inside Popover, Dialog, or Sheet. |
Anatomy
| Slot | Purpose |
|---|---|
command | Inline Base UI Autocomplete root. |
command-input | Required named combobox input; DOM focus remains here. |
command-list | Filtered listbox and grouped result renderer. |
command-group / command-group-label | Accessible labelled result group. |
command-item | Stable action value with optional content slots. |
command-item-leading | General React content that owns its semantics; decorative Nerio Icons hide themselves. |
command-empty / command-loading | Dedicated polite status regions outside listbox children. |
States
<Command defaultQuery="no-match" items={items}>...</Command>| State | Behavior |
|---|---|
Active | aria-activedescendant tracks the keyboard or pointer-highlighted item. |
Disabled | Visible but skipped during keyboard navigation and never selected. |
Empty | One concise polite message after filtering. |
Loading | A single Spinner and localized polite status for consumer-owned async work. |
IME composition | Enter does not select until composition finishes. |
Motion
- Command itself does not animate active-item movement or query updates.
- Popover, Dialog, and Sheet retain their own reduced-motion-aware overlay behavior.
Accessibility
- Uses the Base UI Autocomplete combobox and listbox interaction model while keeping DOM focus on CommandInput through aria-activedescendant.
- CommandInput requires a visible label, aria-label, or aria-labelledby; placeholder text is not an accessible name.
- Arrow keys navigate enabled items, disabled items are skipped, Enter selects the active item, and Escape bubbles to enclosing Popover, Dialog, or Sheet dismissal behavior.
- Home and End preserve native input editing because virtual focus remains on the query field.
- Base UI suppresses Enter selection during IME composition and supports pointer highlighting and selection without moving DOM focus into items.
- Selection writes only the visible item label into controlled or uncontrolled query and form values; keywords and stable values are matching-only text.
- CommandItem leading content owns its accessibility semantics; decorative Nerio Icons remain hidden through their own default contract.
- CommandEmpty and CommandLoading use dedicated polite Base UI status regions and are not arbitrary children of the listbox.
- RTL, long localized labels, compact density, reduced motion, and forced colors are supported through semantics and tokens.
- Fetching, ranking, routing, permissions, analytics, history, global shortcuts, and complete command-palette workflows remain consumer or Pro responsibilities.
API
| API | Purpose |
|---|---|
items | Flat CommandItemData or labelled CommandGroupData records with stable values. |
query / defaultQuery / onQueryChange | Controlled or uncontrolled visible query; selection writes the item label only. |
filter | Typed matcher over leaf items; default search includes label, value, and keywords, or false preserves external results. |
onActiveValueChange | Reports stable highlighted values without exposing internal indices. |
CommandItem.onSelect | Emits the stable value and event while the visible query remains label-only. |
Implementation contract
| Contract | Value |
|---|---|
Registry item | command-primitive installs 7 source files into the configured components directory. |
Base UI | autocomplete |
Registry dependencies | spinner |
Package dependencies | @base-ui/react, @nerio-ui/adapters, clsx, react, tailwind-merge, tailwindcss |
Styling contract
| Contract | Value |
|---|---|
Authoring | Complete, statically detectable Tailwind CSS v4 recipes own component visuals. |
Values | Semantic and component --n-* variables remain the canonical customization layer. |
Overrides | Customizable slots merge consumer className values with tailwindCn so conflicting utilities resolve deterministically. |
Residual CSS | Only shared keyframes and scoped no-Preflight compatibility rules remain; there is no parallel visual selector layer. |
Design notes
- Command is not GlobalSearch, EntitySearch, Documentation Search, or a complete Command Palette.
- Fetching, ranking, routing, permissions, analytics, history, recent items, and global shortcuts stay outside Core.
- Use consumer state to replace items and toggle CommandLoading for asynchronous results.
- Leading content is general React content and owns its semantics; use decorative Nerio Icons when no accessible meaning is needed.
Do / do not
Do
Use for local commands and compact action pickers; compose icons, descriptions, metadata, and Kbd.
Do not
Turn Core Command into GlobalSearch, Documentation Search, a routed palette, remote ranking, history, or global shortcut registration.
Related components
Tokens
These are the primary customization points. Override semantic or component tokens instead of changing component source.
| Token | Purpose |
|---|---|
--n-command-width | Public customization point for this component contract. |
--n-command-list-max-height | Public customization point for this component contract. |
--n-command-input-height | Public customization point for this component contract. |
--n-command-item-height | Public customization point for this component contract. |
--n-command-item-padding-block | Public customization point for this component contract. |
--n-command-item-padding-inline | Public customization point for this component contract. |
--n-command-list-padding | Public customization point for this component contract. |
--n-command-group-gap | Public customization point for this component contract. |
--n-command-group-spacing | Public customization point for this component contract. |
--n-command-item-gap | Public customization point for this component contract. |
--n-command-state-min-height | Public customization point for this component contract. |
--n-command-state-padding | Public customization point for this component contract. |
--n-command-background | Public customization point for this component contract. |
--n-command-foreground | Public customization point for this component contract. |
--n-command-border-width | Public customization point for this component contract. |
--n-command-border | Public customization point for this component contract. |
--n-command-radius | Public customization point for this component contract. |
--n-command-item-radius | Public customization point for this component contract. |
--n-command-item-background-active | Public customization point for this component contract. |
--n-overlay-background | Public customization point for this component contract. |
--n-overlay-foreground | Public customization point for this component contract. |
--n-overlay-foreground-muted | Public customization point for this component contract. |
--n-overlay-control-background | Public customization point for this component contract. |
--n-overlay-surface-filter | Public customization point for this component contract. |
--n-overlay-shadow | Public customization point for this component contract. |
--n-input-foreground-on-overlay | Public customization point for this component contract. |
--n-input-placeholder-on-overlay | Public customization point for this component contract. |
--n-focus-ring | Public customization point for this component contract. |