Skip to content

Components · Command

Command menu

A palette for running things: navigate, act, toggle. cmdk owns filtering, ranking, the active-descendant model and scroll-into-view; LUC owns the palette layout, the search field, the grouping rhythm, the active row language and the shortcut column.

Examples

import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandMenu, CommandSeparator, useCommandPages, } from "@luc/ui";

Open it

The Showcase registers the hotkey globally and the header carries a visible affordance — a shortcut nobody can discover is not a feature.

Trigger

⌘K on macOS, Ctrl K elsewhere. The hook lives in @luc/ui; a product decides when to enable it.

Last action

Nothing yet — open the palette and run something.

Filtering

Type to filter across labels and keywords. Ranking is cmdk's; the markup is ours.

useCommandHotkey(() => setOpen(true));
<CommandMenu open={open} onOpenChange={setOpen} label="Command menu">
  <Command>
    <CommandInput placeholder="Search commands…" />
    <CommandList>
      <CommandGroup heading="Navigate">
        <CommandItem shortcut="G C" onSelect={() => router.push("/charts")}>Go to charts</CommandItem>
      </CommandGroup>
    </CommandList>
  </Command>
</CommandMenu>

Semantics

A command menu runs things. It is not a dropdown.

  • Use Select to pick a known value; the choice is the outcome.
  • Use Combobox when the list is long or comes from a server.
  • Use CommandMenu when the user is invoking behaviour, not choosing a field.
  • Never wire Select to navigation: a select that redirects is a trap.
Command + Dialog: role="dialog" shell with role="listbox" results inside, role="option" rows, aria-activedescendant on the input, Escape closes, Enter runs the highlighted command.

Nested pages

One palette can hold sub-pages: push a page, clear the search, pop with Backspace.

Open the palette below and choose “Switch theme”. The page stack is a hook, not a router: useCommandPages() returns push, pop, reset and the keydown handler.

API

Read from the published TypeScript API, so a prop that is not listed here does not exist and a default that is listed is the one the compiler sees.

Exported symbols
ExportKindSignature
Command11 documented props({ className, label, loop, ...props }: CommandProps) => import("@types/react").JSX.Element
CommandEmpty2 documented props({ className, children, ...props }: React.ComponentPropsWithRef<typeof CmdkCommand.Empty>) => import("@types/react").JSX.Element
CommandGroup5 documented props({ className, heading, ...props }: React.ComponentPropsWithRef<typeof CmdkCommand.Group>) => import("@types/react").JSX.Element
CommandInput3 documented props({ className, ...props }: CommandInputProps) => import("@types/react").JSX.Element
CommandItem10 documented props({ className, children, icon, shortcut, description, ...props }: CommandItemProps) => import("@types/react").JSX.Element
CommandList3 documented props({ className, ...props }: CommandListProps) => import("@types/react").JSX.Element
CommandLoading4 documented props({ className, children, ...props }: React.ComponentPropsWithRef<typeof CmdkCommand.Loading>) => import("@types/react").JSX.Element
CommandMenu7 documented props({ open, onOpenChange, children, label, description, footer, className, }: CommandMenuProps) => import("@types/react").JSX.Element
CommandSeparator2 documented props({ className, ...props }: React.ComponentPropsWithRef<typeof CmdkCommand.Separator>) => import("@types/react").JSX.Element
useCommandHotkeyexport(onOpen: () => void, enabled?: boolean) => void
useCommandPagesexport() => { pages: string[]; page: string | undefined; search: string; setSearch: import("@types/react").Dispatch<import("@types/react").SetStateAction<string>>; push: (next: string) => void; pop: () => …

Command props

Type: Children & Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<...>> & { ...; } & { .…

Values the compiler reads from the declaration.

Command props
PropTypeDefaultNotes
asChild?inheritedboolean | undefined—
children?inheritedReactNode—
defaultValue?inheritedstring | (readonly string[] & string) | undefined—Optional default item value when it is initially rendered.
disablePointerSelection?inheritedboolean | undefined—Optionally set to `true` to disable selection via pointer events.
filter?inheritedCommandFilter | undefined—Custom filter function for whether each command menu item should matches the given search query. It should return a number between 0 and 1, with 1 being the best match and 0 being…
label?inheritedstring | undefined—Accessible label for this command menu. Not shown visibly.
loop?inheritedboolean | undefined—Optionally set to `true` to turn on looping around when using the arrow keys.
onValueChange?inherited((value: string) => void) | undefined—Event handler called when the selected item of the menu changes.
shouldFilter?inheritedboolean | undefined—Optionally set to `false` to turn off the automatic filtering and sorting. If `false`, you must conditionally render valid items based on the search query yourself.
value?inheritedstring | undefined—Optional controlled state of the selected command menu item.
vimBindings?inheritedboolean | undefined—Set to `false` to disable ctrl+n/j/p/k shortcuts. Defaults to `true`.

CommandEmpty props

Type: Children & Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<...>> & { ...; } & { .…

Values the compiler reads from the declaration.

CommandEmpty props
PropTypeDefaultNotes
asChild?inheritedboolean | undefined—
children?inheritedReactNode—

CommandGroup props

Type: Children & Omit<Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<...>> & { ...; } …

Values the compiler reads from the declaration.

CommandGroup props
PropTypeDefaultNotes
asChild?inheritedboolean | undefined—
children?inheritedReactNode—
forceMount?inheritedboolean | undefined—Whether this group is forcibly rendered regardless of filtering.
heading?inheritedReactNode—Optional heading to render for this group.
value?inheritedstring | undefined—If no heading is provided, you must provide a value that is unique for this group.

CommandInput props

Type: Omit<Pick<Pick<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "key" | keyof InputHTMLAttributes<...>> & { ...;…

Values the compiler reads from the declaration.

CommandInput props
PropTypeDefaultNotes
asChild?inheritedboolean | undefined—
onValueChange?inherited((search: string) => void) | undefined—Event handler called when the search value changes.
value?inheritedstring | undefined—Optional controlled state for the value of the search input.

CommandItem props

Type: CommandItemProps

Values the compiler reads from the declaration.

CommandItem props
PropTypeDefaultNotes
asChild?inheritedboolean | undefined—
children?inheritedReactNode—
description?ReactNode—Optional secondary line.
disabled?inheritedboolean | undefined—Whether this item is currently disabled.
forceMount?inheritedboolean | undefined—Whether this item is forcibly rendered regardless of filtering.
icon?ReactNode—Leading icon. Decorative — the label carries the meaning.
keywords?inheritedstring[] | undefined—Optional keywords to match against when filtering.
onSelect?inherited((value: string) => void) | undefined—Event handler for when this item is selected, either via click or keyboard selection.
shortcut?ReactNode—Trailing keyboard hint, e.g. `"⌘K"`.
value?inheritedstring | undefined—A unique value for this item. If no value is provided, it will be inferred from `children` or the rendered `textContent`. If your `textContent` changes between renders, you _must_…

CommandList props

Type: Children & Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<...>> & { ...; } & { .…

Values the compiler reads from the declaration.

CommandList props
PropTypeDefaultNotes
asChild?inheritedboolean | undefined—
children?inheritedReactNode—
label?inheritedstring | undefined—Accessible label for this List of suggestions. Not shown visibly.

CommandLoading props

Type: Children & Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<...>> & { ...; } & { .…

Values the compiler reads from the declaration.

CommandLoading props
PropTypeDefaultNotes
asChild?inheritedboolean | undefined—
children?inheritedReactNode—
label?inheritedstring | undefined—Accessible label for this loading progressbar. Not shown visibly.
progress?inheritednumber | undefined—Estimated progress of loading asynchronous options.

CommandMenu props

Type: CommandMenuProps

Values the compiler reads from the declaration.

CommandMenu props
PropTypeDefaultNotes
childrenReactNode—
className?string | undefined—
description?ReactNode—Description under the search field — context, not decoration.
footer?ReactNode—Footer strip: hints, counts, or a live status line.
label?string | undefined—Accessible name for the listbox.
onOpenChange(open: boolean) => void—
openboolean—

CommandSeparator props

Type: Pick<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> & { ...; } & { .…

Values the compiler reads from the declaration.

CommandSeparator props
PropTypeDefaultNotes
alwaysRender?inheritedboolean | undefined—Whether this separator should always be rendered. Useful if you disable automatic filtering.
asChild?inheritedboolean | undefined—

Usage

import { Command, CommandInput, CommandList, CommandGroup, CommandItem, CommandEmpty } from "@luc/ui/components/command";
<Command>
  <CommandInput placeholder="Search actions…" />
  <CommandList>
    <CommandGroup heading="Actions">
      <CommandItem>Export the run</CommandItem>
    </CommandGroup>
    <CommandEmpty>No matching action.</CommandEmpty>
  </CommandList>
</Command>

Guidelines

Do

  • Use it for a searchable set of actions the reader already knows by name; the query filters the list as they type.
  • Group results and keep `CommandEmpty` written for the empty case — a blank list reads as a broken search.

Don't

  • Use it as a navigation menu that is always open; a palette is summoned, and `CommandMenu` plus `useCommandHotkey` is the composed form.

Accessibility

The list is a `listbox` with the input as its combobox: Arrow keys move the active option, Enter chooses it, and the active option is announced as it changes. `CommandEmpty` is a live region, so an empty result is spoken.

Luc UI v1.0Commanddark theme