Skip to main content

Overview

ESC
Navigation
Actions
↑↓ navigate↵ selectESC close
Command is a keyboard-first command palette — typically opened with ⌘K — that allows users to search, navigate, and trigger actions without leaving the keyboard. It supports grouped results, icons, and keyboard shortcuts.

Installation

npm install @araf-ds/core

Usage

import {
  Command,
  CommandDialog,
  CommandInput,
  CommandList,
  CommandEmpty,
  CommandGroup,
  CommandItem,
  CommandShortcut,
  CommandSeparator,
} from "@araf-ds/core"

export default function Example() {
  const [open, setOpen] = useState(false)

  // Open with ⌘K
  useEffect(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
        setOpen(prev => !prev)
      }
    }
    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="Navigation">
          <CommandItem>
            <HomeIcon size={14} />
            Home
            <CommandShortcut>G H</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <UserIcon size={14} />
            Profile
            <CommandShortcut>G P</CommandShortcut>
          </CommandItem>
        </CommandGroup>
        <CommandSeparator />
        <CommandGroup heading="Actions">
          <CommandItem>
            <PlusIcon size={14} />
            New document
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </CommandDialog>
  )
}

Variants

Inline (Embedded in Page)

Use Command directly (without CommandDialog) to embed the palette inline — useful for search inputs, comboboxes, and filter panels.
Search frameworks…
<Command className="rounded-lg border shadow-md w-[320px]">
  <CommandInput placeholder="Search frameworks..." />
  <CommandList>
    <CommandEmpty>No framework found.</CommandEmpty>
    <CommandGroup>
      {frameworks.map(fw => (
        <CommandItem
          key={fw.value}
          value={fw.value}
          onSelect={setValue}
        >
          <CheckIcon
            className={cn(value === fw.value ? "opacity-100" : "opacity-0")}
            size={12}
          />
          {fw.label}
        </CommandItem>
      ))}
    </CommandGroup>
  </CommandList>
</Command>

Dialog (Global ⌘K Palette)

The most common pattern — full-screen overlay opened with a keyboard shortcut.
// Trigger button shown in the UI
<Button
  variant="outline"
  onClick={() => setOpen(true)}
  className="gap-2 text-muted-foreground"
>
  <SearchIcon size={14} />
  Search...
  <kbd className="ml-auto text-xs border rounded px-1">⌘K</kbd>
</Button>

<CommandDialog open={open} onOpenChange={setOpen}>
  <CommandInput placeholder="Type a command or search..." />
  <CommandList>
    <CommandEmpty>No results found.</CommandEmpty>
    <CommandGroup heading="Suggestions">
      <CommandItem onSelect={() => router.push("/dashboard")}>
        <LayoutDashboardIcon size={14} /> Dashboard
      </CommandItem>
      <CommandItem onSelect={() => router.push("/settings")}>
        <SettingsIcon size={14} /> Settings
        <CommandShortcut>⌘,</CommandShortcut>
      </CommandItem>
    </CommandGroup>
  </CommandList>
</CommandDialog>

API Reference

Command

value
string
Controlled selected item value.
onValueChange
(value: string) => void
Callback fired when the selected item changes.
filter
(value: string, search: string) => number
Custom filter function. Return 1 to show, 0 to hide. Default: fuzzy search.
shouldFilter
boolean
default:"true"
Set to false to disable built-in filtering (handle externally with async search).

CommandDialog

open
boolean
Controlled open state.
onOpenChange
(open: boolean) => void
Callback fired when open state changes.

CommandInput

placeholder
string
Placeholder text shown when empty.
value
string
Controlled input value.

CommandItem

value
string
The value used for filtering and selection.
onSelect
(value: string) => void
Callback fired when the item is selected (Enter or click).
disabled
boolean
default:"false"
Disables the item.

CommandGroup

heading
string
Group label shown above the items.

Accessibility

  • CommandDialog uses role="dialog" with aria-modal="true"
  • CommandInput is a search input with role="combobox" and aria-expanded
  • CommandList has role="listbox"; each CommandItem has role="option"
  • Keyboard: Arrow Up/Down navigates, Enter selects, Escape closes, typing filters
  • CommandEmpty is announced by screen readers when no results match

Do’s & Don’ts

Do

  • Register ⌘K / Ctrl+K globally to open the command palette
  • Group items with CommandGroup and clear heading labels
  • Show a visible keyboard shortcut hint in the UI (e.g. a button with ⌘K badge)
  • Use shouldFilter={false} for server-side or async search

Don't

  • Don’t put destructive actions in the command palette without confirmation
  • Don’t list more than 20–30 items without grouping or filtering
  • Don’t use Command as a regular dropdown — use Select or DropdownMenu
  • Don’t forget to close the dialog after an action is selected