Command

Filters and ranks commands as users type, with an optional floating palette.

import { Command } from "@usefragments/ui";Source

Preview

<Command>
  <Command.Input placeholder="Type a command…" />
  <Command.List>
    <Command.Item onSelect={() => {}}>Open File</Command.Item>
    <Command.Item onSelect={() => {}}>Save Document</Command.Item>
    <Command.Item onSelect={() => {}}>Print</Command.Item>

Usage

Use when

  • A keyboard shortcut that jumps anywhere
  • Action lists too long to scan
  • Inline lists that need filtering
  • Search across everything the app can do

Avoid when

  • A handful of actions (use Menu)
  • Picking a value for a form (use Select or Combobox)
  • Fixed navigation (use Sidebar or Tabs)

Implementation notes

  • Use Command.Dialog for the summon-anywhere palette; Command itself is frameless and sits in a Popover or a page as is
  • Group by category once the list gets long; group labels separate groups, never lines
  • Always supply Command.Empty
  • Add keywords so aliases and synonyms match
  • Label rows with a verb
  • Pass loading to Command.List while results load, and Command.Error with onRetry when they fail

Installation

pnpm add @usefragments/ui

Import styles once from @usefragments/ui/styles. See the installation guide for framework and theme setup.

Anatomy

Import Command once, then compose its parts with dot notation.

  • Command.Input
  • Command.List
  • Command.Item
  • Command.Group
  • Command.Empty
  • Command.Error
  • Command.Separator
  • Command.Dialog

Search row over a flat list.

<Command>
  <Command.Input placeholder="Type a command…" />
  <Command.List>
    <Command.Item onSelect={() => {}}>Open File</Command.Item>
    <Command.Item onSelect={() => {}}>Save Document</Command.Item>
    <Command.Item onSelect={() => {}}>Print</Command.Item>
    <Command.Empty>No results found.</Command.Empty>
  </Command.List>
</Command>

Examples

Palette

Command.Dialog: a scrim and the raised panel near the top, key hints underneath; running an item closes it.

const [open, setOpen] = React.useState(false);

<Command.Dialog open={open} onOpenChange={setOpen}>
  <Command.Input placeholder="Search commands…" />
  <Command.List>
    <Command.Group heading="Actions">

With Groups

Category labels survive filtering.

<Command>
  <Command.Input placeholder="Search…" />
  <Command.List>
    <Command.Group heading="Suggestions">
      <Command.Item onSelect={() => {}}>Calendar</Command.Item>
      <Command.Item onSelect={() => {}}>Calculator</Command.Item>

Loading and error

A busy list shows Searching after a second; a failed load offers Retry.

const loading = false;
const [failed, setFailed] = React.useState(true);
const reload = () => setFailed(false);

<Command>
  <Command.Input placeholder="Search issues…" />

API reference

Component props
Prop
Type
Default
Description
childrenRequiredReact.ReactNodeNot setCommand.Input, Command.List, and other sub-components
defaultSearchstring | undefinedDefault search value (uncontrolled)
Dialog.childrenRequiredReact.ReactNodeNot set—
Dialog.defaultOpenboolean | undefinedNot setDefault open state
Dialog.defaultSearchstring | undefinedNot setDefault search value
Dialog.filterCommandFilter | undefinedNot setRanks an item for a search: 0 hides it, and higher scores sort first. Default: a prefix beats a word start, which beats a substring, which beats a keyword.
Dialog.footerReact.ReactNodeNot setThe key hints under the list. Pass `null` to leave them out.
Dialog.labelstring | undefinedNot setThe palette's accessible name.
Dialog.loopboolean | undefinedNot setWhether arrow keys wrap from the last item to the first.
Dialog.onOpenChange((open: boolean) => void) | undefinedNot setCalled when the open state changes
Dialog.onSearchChange((search: string) => void) | undefinedNot setCalled when search input changes
Dialog.openboolean | undefinedNot setControlled open state
Dialog.searchstring | undefinedNot setControlled search value
Empty.childrenRequiredReact.ReactNodeNot set—
Error.childrenRequiredReact.ReactNodeNot setWhat went wrong, in one sentence
Error.onRetry(() => void) | undefinedNot setAdds a Retry button
Error.retryLabelstring | undefinedNot setLabel for the retry button.
filterCommandFilter | undefinedNot setRanks an item for a search: 0 hides it, higher scores sort first. The default ranks a prefix over a word start over a substring over a keyword.
Group.childrenRequiredReact.ReactNodeNot set—
Group.headingstring | undefinedNot setGroup heading text
Input.classNamestring | undefinedNot set—
Item.childrenRequiredReact.ReactNodeNot set—
Item.disabledboolean | undefinedNot setWhether this item is disabled
Item.keywordsstring[] | undefinedNot setExtra keywords for filtering
Item.onSelect((value: string, event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => void) | undefinedNot setRuns the item (Return or a press). Inside `Command.Dialog` the palette closes after it, unless the handler calls `event.preventDefault()`.
Item.valuestring | undefinedNot setValue used for filtering (falls back to text content)
List.childrenRequiredReact.ReactNodeNot set—
List.loadingboolean | undefinedNot setResults are loading: the list is busy, and after a second a spinner row shows.
List.loadingLabelstring | undefinedNot setThe loading row's words.
loopboolean | undefinedtrueWhether to loop keyboard navigation
onSearchChange((search: string) => void) | undefinedNot setCalled when search input changes
searchstring | undefinedNot setControlled search value

Accessibility

  • Combobox and listbox pattern: aria-expanded follows whether any result shows
  • The first match is active after every search change, so Return runs it
  • ArrowUp/ArrowDown move in ranked order, Home/End jump to first/last
  • aria-activedescendant tracks the active row; Empty, Searching and the error row are not options
  • The error row is a polite status with a danger glyph beside the words, so colour is never the only signal
  • DialogsiblingCommand.Dialog is the palette; plain Dialog is for tasks with a form
  • ListboxsiblingCommand uses Listbox-like keyboard navigation internally
  • ComboboxalternativeUse Combobox for form field selection with search

Next steps