Skip to Content
ComponentsCommand

Command

A searchable list of things to do: a command palette, or the list inside a combobox. Built on cmdk , which owns the filtering, the scoring and the keyboard model.

Live Examples

Live Example
☀️
Copy
View Code
import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut } from '@jarllyng/nostromo' export default function CommandExample() { return ( <Command className="max-w-md rounded-lg border border-border"> <CommandInput placeholder="Type a command or search" /> <CommandList> <CommandEmpty>No results found.</CommandEmpty> <CommandGroup heading="Files">...

Type nf. It finds “New file”, because the match is a fuzzy score rather than a substring.

As a palette

Live Example
☀️
Copy
View Code
import { CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, Button } from '@jarllyng/nostromo' export default function CommandPalette() { const [open, setOpen] = React.useState(false) React.useEffect(() => { const onKeyDown = (e: KeyboardEvent) => { if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { e.preventDefault() setOpen((o) => !o)...

The ⌘K listener is yours to add. It is a page-level keyboard shortcut, and a component that installed a global listener on mount would fight with whatever else the page wants that key for.

Import

import { Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut, } from "@jarllyng/nostromo";

Parts

PartWhat it is
CommandThe root. Holds the query and the filtered results
CommandDialogThe same thing in a dialog, with a hidden title
CommandInputThe search field. role="combobox"
CommandListThe results. role="listbox"
CommandEmptyShown when a query matches nothing
CommandGroupA named section. Hides itself when all its items filter out
CommandItemOne option. role="option"
CommandSeparatorA line between groups. Decorative
CommandShortcutThe keyboard hint on the right of an item. Decorative
CommandLoadingShown while server-side results are on the way

Props

Command

PropTypeDefaultDescription
labelstring-Accessible name for the list
valuestring-Controlled selected item value
onValueChange(value: string) => void-Fires when the selection moves
shouldFilterbooleantrueTurn off when the results arrive already filtered
filter(value, search, keywords) => number-Replace the scoring function
loopbooleanfalseArrow keys wrap around the ends

CommandItem

PropTypeDefaultDescription
valuestringThe item’s textWhat the query is matched against
keywordsstring[]-Extra terms that should find it
onSelect(value: string) => void-Fires on Enter or click
disabledbooleanfalseNot selectable

CommandDialog takes everything Dialog takes, plus title and description.

Not a DropdownMenu

A DropdownMenu is a menu: role="menu", items you activate, no typing. This is a combobox over a listbox, which is what a screen reader needs to hear when the list narrows as you type. Filtering a menu would leave a screen reader announcing a menu whose contents silently change underneath it.

Choosing from a short fixed set of commands is DropdownMenu. Searching is this.

Filtering is fuzzy, and it is cmdk’s

Each item is scored against the query, so gh finds GitHub and the best match sorts to the top. Items match on their value, which defaults to their text.

  • Give value explicitly when the text is not what someone would type.
  • Add keywords for synonyms: <CommandItem value="sign-out" keywords={['logout', 'log out']}>.
  • Pass shouldFilter={false} when the list comes from a server and is already filtered, or cmdk will filter the results a second time.

Name the dialog

CommandDialog renders a visually hidden title and description, defaulting to “Command palette” and “Search for a command to run”. A palette shows a search field rather than a heading, so without them the dialog is announced as “dialog” and nothing else. Override them to match your product’s language.

The corner close button is deliberately absent: it is positioned at the top right, which is where the search field is. Escape and the backdrop still close it.

Keyboard

Arrow up and down move the selection, Enter runs the selected item, and typing filters. Home and End go to the ends. Set loop to wrap at the ends.

Last updated on