Light

Command

Fast, composable, unstyled command menu for React.

Spec · from metadata

When to use

  • Command palette (Cmd+K / Ctrl+K) for quick actions
  • Searchable action menu with keyboard navigation
  • Inline command list without a dialog

When not to use

  • Searchable select/dropdown — use Combobox instead
  • Simple dropdown menu — use DropdownMenu instead
  • Static navigation — use regular links or NavigationMenu

Anti-patterns

Avoid<Dialog>
  <DialogContent>
    <Command>
      <CommandInput />
      <CommandList>...</CommandList>
    </Command>
  </DialogContent>
</Dialog>
Prefer<CommandDialog open={open} onOpenChange={setOpen}>
  <CommandInput placeholder="Search..." />
  <CommandList>...</CommandList>
</CommandDialog>

Use CommandDialog for dialog patterns — it handles Dialog wrapping and accessible title internally.

Avoid<Command>
  <CommandGroup>
    <CommandItem>Action</CommandItem>
  </CommandGroup>
</Command>
Prefer<Command>
  <CommandInput placeholder="Search..." />
  <CommandList>
    <CommandGroup heading="Actions">
      <CommandItem>Action</CommandItem>
    </CommandGroup>
  </CommandList>
</Command>

Always include CommandInput and wrap groups in CommandList for proper scrolling and filtering.

Accessibility

  • Screen readercmdk handles ARIA listbox, option, and combobox roles. CommandDialog includes sr-only title and description.
  • ContrastSelected items use bg-accent/text-accent-foreground. Disabled items are 50% opacity.

Token bindings

TokenCategoryUsage
popovercolorCommand background
popover-foregroundcolorCommand text
accentcolorSelected item background
accent-foregroundcolorSelected item text
muted-foregroundcolorGroup headings and shortcut text
bordercolorSeparator and input border

Import

import {
  Command,
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from "@timelycare/helix-ui"

Props

interface CommandProps {
  className?: string
  children: React.ReactNode
}

interface CommandInputProps {
  placeholder?: string
  value?: string
  onValueChange?: (value: string) => void
  className?: string
}

interface CommandItemProps {
  value?: string
  disabled?: boolean
  onSelect?: () => void
  className?: string
  children: React.ReactNode
}

interface CommandGroupProps {
  heading?: string
  className?: string
  children: React.ReactNode
}

Variants

VariantUse For
SuggestionsFull command menu with grouped actions
EmptyNo results state
Dialog⌘K command palette overlay

Item Types

TypeUse ForElements
DefaultText-only commandsText + optional check
IconCommands with leading iconIcon + text + optional shortcut

Styling

Container

PropertyValueTailwind
Max Width512pxmax-w-lg
Backgroundwhitebg-popover
Border1px solidborder border-border
Border Radius8pxrounded-md
Shadowmdshadow-md
Overflowhiddenoverflow-hidden

Command Input

PropertyValueTailwind
Height48pxh-12
Padding12pxp-3
Gap8pxgap-2
Backgroundtransparentbg-transparent
BorderBottom onlyborder-b border-border
Search Icon16px, 50% opacitysize-4 opacity-50
Clear Icon~11pxsize-[11px]
Text14px/20px Regulartext-sm
Placeholder Color#646565text-muted-foreground

Command Group

PropertyValueTailwind
Padding8px horizontal, 4px verticalpx-2 py-1

Group Heading

PropertyValueTailwind
Padding8px horizontal, 6px verticalpx-2 py-1.5
FontAdelle Sans Semiboldfont-semibold
Size12px/16pxtext-xs leading-4
Color#646565text-muted-foreground
Overflowellipsistruncate

Command Item

PropertyValueTailwind
Padding8px horizontal, 12px verticalpx-2 py-3
Gap8pxgap-2
Border Radius6pxrounded-sm
Text14px/20px Regulartext-sm font-normal
Icon Size16pxsize-4
Icon Color#646565text-muted-foreground

Command Shortcut

PropertyValueTailwind
FontAdelle Sans Regularfont-normal
Size12px/16pxtext-xs
Color#646565text-muted-foreground
AlignmentRightml-auto

Command Separator

PropertyValueTailwind
Height1pxh-px
Color#e4e4e7bg-border
Margin1px horizontalmx-px

Command Empty

PropertyValueTailwind
Padding24px verticalpy-6
Text14px/20px Regulartext-sm
Color#09090btext-popover-foreground
AlignmentCenteredtext-center

States

StateBackgroundText ColorOther
Defaulttransparenttext-foreground-
Hoverbg-accent (#e4eefa)text-accent-foreground (#19518b)-
Selected--Check icon visible
Disabled--opacity-50 pointer-events-none

Icons

Item Icon Size: size-4 (16×16px) with text-muted-foreground

Check Icon Size: size-4 (16×16px)

IconUse For
SearchInput search icon (with opacity-50)
XClear input icon (~11px)
CheckSelected indicator
Any Lucide iconCommand item icons

Common Patterns

Basic Command Menu

<Command className="rounded-md border shadow-md">
  <CommandInput placeholder="Type a command or search..." />
  <CommandList>
    <CommandEmpty>No results found.</CommandEmpty>
    <CommandGroup heading="Suggestions">
      <CommandItem>
        <Calendar className="size-4 text-muted-foreground" />
        Calendar
      </CommandItem>
      <CommandItem>
        <Smile className="size-4 text-muted-foreground" />
        Search Emoji
      </CommandItem>
      <CommandItem>
        <Calculator className="size-4 text-muted-foreground" />
        Calculator
      </CommandItem>
    </CommandGroup>
  </CommandList>
</Command>

With Keyboard Shortcuts

<CommandItem className="gap-2">
  <User className="size-4 text-muted-foreground" />
  Profile
  <CommandShortcut>⌘P</CommandShortcut>
</CommandItem>

Multiple Groups with Separator

<Command>
  <CommandInput placeholder="Type a command..." />
  <CommandList>
    <CommandGroup heading="Suggestions">
      <CommandItem>Calendar</CommandItem>
      <CommandItem>Search Emoji</CommandItem>
    </CommandGroup>
    <CommandSeparator />
    <CommandGroup heading="Settings">
      <CommandItem>Profile</CommandItem>
      <CommandItem>Billing</CommandItem>
      <CommandItem>Settings</CommandItem>
    </CommandGroup>
  </CommandList>
</Command>

Command Dialog (⌘K Pattern)

function CommandPalette() {
  const [open, setOpen] = useState(false)

  useEffect(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
        setOpen((open) => !open)
      }
    }
    document.addEventListener("keydown", down)
    return () => document.removeEventListener("keydown", down)
  }, [])

  return (
    <CommandDialog open={open} onOpenChange={setOpen}>
      <CommandInput placeholder="Type a command or search..." />
      <CommandList>
        <CommandEmpty>No results found.</CommandEmpty>
        <CommandGroup heading="Suggestions">
          <CommandItem onSelect={() => setOpen(false)}>
            <Calendar className="size-4 text-muted-foreground" />
            Calendar
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </CommandDialog>
  )
}

With Selection State

const [selectedValue, setSelectedValue] = useState("")

<Command>
  <CommandInput placeholder="Search..." />
  <CommandList>
    <CommandGroup heading="Options">
      {options.map((option) => (
        <CommandItem
          key={option.value}
          onSelect={() => setSelectedValue(option.value)}
          className="gap-2"
        >
          {option.label}
          <Check
            className={cn(
              "ml-auto size-4",
              selectedValue === option.value ? "opacity-100" : "opacity-0"
            )}
          />
        </CommandItem>
      ))}
    </CommandGroup>
  </CommandList>
</Command>

Accessibility

  • ARIA combobox/listbox pattern
  • Arrow Up/Down navigates items
  • Enter selects current item
  • Escape closes dialog
  • Type-to-search filters in real-time
  • Screen readers announce item count and selection

Gotchas

ProblemSolution
Items not filteringCommandInput filters by value prop on CommandItem
Dialog not closing on selectAdd onSelect={() => setOpen(false)} to items
Shortcut not aligned rightCommandShortcut uses ml-auto for alignment
Need custom empty stateUse <CommandEmpty>Your message</CommandEmpty>
⌘K not workingEnsure keyboard listener is set up and not prevented elsewhere
Icon not colored correctlyAdd text-muted-foreground to icons
Check mark always visibleUse conditional opacity: opacity-100 when selected, opacity-0 otherwise

See Also


Last updated: February 9, 2026