Shadcn Command for React and Tailwind
Searchable command input with filtering.
Installation
bunx --bun shadcn@latest add https://kit.dev/r/command.jsonpnpm dlx shadcn@latest add https://kit.dev/r/command.jsonnpx shadcn@latest add https://kit.dev/r/command.jsonyarn shadcn@latest add https://kit.dev/r/command.json<Step>This component depends on Combobox, Dialog, Input, Input Group, Menu, and Separator. Install them first if you haven't already.</Step>
Install the following dependencies:
bun add @ark-ui/react lucide-reactpnpm add @ark-ui/react lucide-reactnpm install @ark-ui/react lucide-reactyarn add @ark-ui/react lucide-reactCopy and paste the following code into your project.
"use client";
import { Combobox as ArkCombobox } from "@ark-ui/react/combobox";
import { Dialog as ArkDialog } from "@ark-ui/react/dialog";
import { Portal } from "@ark-ui/react/portal";
import { SearchIcon } from "lucide-react";
import type React from "react";
import { cn } from "@/lib/utils";
import {
Combobox,
ComboboxContext,
ComboboxControl,
ComboboxEmpty,
ComboboxGroup,
ComboboxGroupLabel,
ComboboxItemContext,
ComboboxList,
ComboboxRootProvider,
comboboxItemVariants,
useCombobox,
useComboboxContext,
useComboboxItemContext,
} from "@/components/ui/combobox";
import {
Dialog,
type DialogContent,
DialogHeader,
DialogOverlay,
DialogPositioner,
DialogTrigger,
dialogContentVariants,
} from "@/components/ui/dialog";
import type { InputProps } from "@/components/ui/input";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
import { MenuShortcut } from "@/components/ui/menu";
import { Separator } from "@/components/ui/separator";
export const useCommand: typeof useCombobox = (props) =>
useCombobox({
closeOnSelect: false,
disableLayer: true,
inputBehavior: "autohighlight",
loopFocus: false,
open: true,
selectionBehavior: "clear",
...props,
});
export const useCommandContext = useComboboxContext;
export const useCommandItemContext = useComboboxItemContext;
export const CommandContext: typeof ComboboxContext = ComboboxContext;
export const CommandItemContext: typeof ComboboxItemContext =
ComboboxItemContext;
const commandRootClassName = cn(
"isolate",
"flex min-h-0 flex-1 flex-col",
"p-2",
"bg-popover",
"text-popover-foreground",
"rounded-2xl border"
);
export const CommandDialog = Dialog;
export const CommandDialogTrigger = (
props: React.ComponentProps<typeof DialogTrigger>
) => <DialogTrigger data-slot="command-dialog-trigger" {...props} />;
interface CommandDialogContentProps
extends React.ComponentProps<typeof DialogContent> {
/**
* The description of the dialog
*
* @default "Search for a command to run..."
*/
description?: string;
/**
* The title of the dialog
*
* @default "Command Palette"
*/
title?: string;
}
export const CommandDialogContent = (props: CommandDialogContentProps) => {
const {
size = "lg",
title = "Command Palette",
description = "Search for a command to run...",
className,
children,
showCloseButton: _showCloseButton,
bottomStickOnMobile,
positionerClassName,
...rest
} = props;
return (
<Portal>
<DialogOverlay />
<DialogPositioner
className={cn(
bottomStickOnMobile &&
"max-sm:grid-rows-[1fr_auto] max-sm:p-0 max-sm:pt-12",
positionerClassName
)}
>
<ArkDialog.Content
className={cn(
"max-sm:row-start-1",
dialogContentVariants({ bottomStickOnMobile, size }),
"border-0 p-0",
className
)}
data-slot="command-dialog-content"
{...rest}
>
<DialogHeader
className="sr-only"
description={description}
title={title}
/>
{children}
</ArkDialog.Content>
</DialogPositioner>
</Portal>
);
};
export const Command: ArkCombobox.RootComponent = (props) => {
const { lazyMount = true, unmountOnExit = true, className, ...rest } = props;
return (
<Combobox
className={cn(commandRootClassName, className)}
closeOnSelect={false}
data-slot="command"
disableLayer
inputBehavior="autohighlight"
lazyMount={lazyMount}
loopFocus={false}
open
selectionBehavior="clear"
unmountOnExit={unmountOnExit}
{...rest}
/>
);
};
export const CommandRootProvider: ArkCombobox.RootProviderComponent = (
props
) => {
const { lazyMount = true, unmountOnExit = true, className, ...rest } = props;
return (
<ComboboxRootProvider
className={cn(commandRootClassName, className)}
data-slot="command"
lazyMount={lazyMount}
unmountOnExit={unmountOnExit}
{...rest}
/>
);
};
interface CommandInputProps
extends Omit<React.ComponentProps<typeof ArkCombobox.Input>, "size"> {
/**
* The size of the input
*
* @default "md"
*/
size?: InputProps["size"];
}
export const CommandContent = (
props: React.ComponentProps<typeof ArkCombobox.Content>
) => {
const { className, ...rest } = props;
return (
<ArkCombobox.Content
className={cn(
"flex flex-1 flex-col",
"max-h-(--available-height) min-h-0",
"-me-2",
"outline-none",
"scrollbar-thin scrollbar-track-transparent scrollbar-thumb-foreground/20 overflow-auto overscroll-contain",
"[:not(.has-[+[data-slot=command-footer]])]:rounded-b-2xl [:not(.has-[+[data-slot=command-footer]])]:border-b",
className
)}
data-slot="command-content"
{...rest}
/>
);
};
export const CommandInput = (props: CommandInputProps) => {
const { size = "md", className, placeholder, ...rest } = props;
return (
<ComboboxControl className="mb-2">
<InputGroup
className={cn("rounded-xl bg-input/32", className)}
size={size}
>
<InputGroupAddon>
<SearchIcon aria-hidden="true" className="opacity-64" />
</InputGroupAddon>
<ArkCombobox.Input asChild>
<InputGroupInput
autoFocus
data-slot="command-input"
placeholder={placeholder}
{...rest}
aria-label={rest["aria-label"] ?? placeholder ?? "Search"}
/>
</ArkCombobox.Input>
</InputGroup>
</ComboboxControl>
);
};
interface CommandListProps extends React.ComponentProps<typeof ComboboxList> {}
export const CommandList = (props: CommandListProps) => {
const { className, ...rest } = props;
return (
<div className="max-h-72 min-h-0 flex-1">
<ComboboxList
className={cn("flex-1 pe-2.5", className)}
data-slot="command-list"
{...rest}
/>
</div>
);
};
export const CommandEmpty = (
props: React.ComponentProps<typeof ComboboxEmpty>
) => {
const { className, children, ...rest } = props;
return (
<ComboboxEmpty
className={cn("py-6 text-center text-sm", className)}
data-slot="command-empty"
{...rest}
>
{children || "No results found."}
</ComboboxEmpty>
);
};
export const CommandGroup = (
props: React.ComponentProps<typeof ComboboxGroup>
) => <ComboboxGroup data-slot="command-group" {...props} />;
export const CommandGroupLabel = (
props: React.ComponentProps<typeof ComboboxGroupLabel>
) => <ComboboxGroupLabel data-slot="command-group-label" {...props} />;
export const CommandItem = (
props: React.ComponentProps<typeof ArkCombobox.Item>
) => {
const { className, ...rest } = props;
return (
<ArkCombobox.Item
className={cn(comboboxItemVariants({ showIndicator: false }), className)}
data-slot="command-item"
persistFocus
{...rest}
/>
);
};
export const CommandSeparator = (props: React.ComponentProps<"div">) => {
const { className, ...rest } = props;
return (
<Separator
className={cn("my-2", className)}
data-slot="command-separator"
{...rest}
/>
);
};
export const CommandShortcut = (
props: React.ComponentProps<typeof MenuShortcut>
) => <MenuShortcut data-slot="command-shortcut" {...props} />;
export const CommandFooter = (props: React.ComponentProps<"div">) => {
const { className, ...rest } = props;
return (
<div
className={cn(
"z-10",
"flex items-center justify-between gap-2",
"-m-2 mt-2 px-4 py-3",
"bg-muted/48",
"text-muted-foreground text-xs",
"rounded-b-[calc(var(--radius-2xl,1rem)-1px)] border-t",
className
)}
data-slot="command-footer"
{...rest}
/>
);
};Update the import paths to match your project setup.
Anatomy
Command
├── CommandInput
│ ├── ComboboxControl (built in)
│ └── search icon (built in)
├── CommandContent
│ ├── CommandEmpty
│ └── CommandList
│ └── CommandGroup
│ ├── CommandGroupLabel
│ └── CommandItem
│ └── CommandShortcut (optional)
├── CommandSeparator
└── CommandFooter (optional)
CommandDialog
├── CommandDialogTrigger
└── CommandDialogContent
└── CommandCommand is Combobox with palette defaults: the list stays open, selection clears the input, and the first match is highlighted while typing. CommandContent is inline (not portaled). useCommand is the machine hook for CommandRootProvider; useCommandContext / CommandContext is in-tree.
CommandDialog is Dialog. CommandDialogContent portals overlay + positioner, hides the title and description visually (sr-only), and does not render a close button.
Usage
import { useListCollection } from "@ark-ui/react/collection";
import { useFilter } from "@ark-ui/react/locale";
import {
Command,
CommandContent,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";const { contains } = useFilter({ sensitivity: "base" });
const { collection, filter } = useListCollection({
initialItems: [
{ label: "Search", value: "search", group: "Actions" },
{ label: "Settings", value: "settings", group: "Actions" },
],
filter: contains,
groupBy: (item) => item.group,
});
<Command
collection={collection}
onInputValueChange={({ inputValue }) => filter(inputValue)}
>
<CommandInput placeholder="Search..." />
<CommandContent>
<CommandEmpty />
<CommandList>
{collection.group().map(([group, items]) => (
<CommandGroup heading={group} key={group}>
{items.map((item) => (
<CommandItem item={item} key={item.value}>
{item.label}
</CommandItem>
))}
</CommandGroup>
))}
</CommandList>
</CommandContent>
</Command>shadcn.io Command defaults (vs Combobox / Ark): open true, closeOnSelect false, disableLayer true, inputBehavior "autohighlight", loopFocus false, selectionBehavior "clear", lazyMount / unmountOnExit true. Selected values are always a string[].
Controlled
Control the last selected command with value and onValueChange. Selected values are always a string[]. selectionBehavior="clear" still clears the input after select.
Root Provider
Use useCommand with CommandRootProvider when you need the API outside the tree. useCommand() applies the same palette defaults as Command. Pass collection and handlers to useCommand(), not to the provider.
Examples
With Dialog
Wrap Command in CommandDialog. Close the dialog from onValueChange after a command is chosen.
Groups
Group related items with groupBy on the collection and collection.group() when rendering. CommandGroup accepts a heading for the label. Put CommandSeparator between groups as a sibling (give the wrapping fragment a key).
Shortcuts
Display keyboard shortcuts next to items with CommandShortcut.
With Footer
Add hints or actions in CommandFooter. Place it after CommandContent so the list can round and border correctly.
Scrollable
CommandList caps height at max-h-72. Long collections scroll inside the list.
Context
Read highlighted and selected state with CommandContext or useCommandContext.
Guides
Command vs Combobox
| Command | Combobox | Autocomplete | |
|---|---|---|---|
| Purpose | Run an action | Pick from a known set | Free text with suggestions |
| List | Always open | Popover | Popover |
| Input behavior | autohighlight | none | autocomplete |
| After select | Clears input | Replaces input | Replaces input |
| Trigger | None (search field) | Chevron | Hidden |
Use Menu for a short action list without search. Use Select when there is no search and the value stays on the control.
Dialog palettes
Keep CommandDialog mounted and control it with open / onOpenChange. Close it from onValueChange when a command should run and dismiss:
<CommandDialog onOpenChange={(e) => setOpen(e.open)} open={open}>
<CommandDialogTrigger asChild>
<Button>Open</Button>
</CommandDialogTrigger>
<CommandDialogContent>
<Command
collection={collection}
onInputValueChange={({ inputValue }) => filter(inputValue)}
onValueChange={() => setOpen(false)}
>CommandDialogContent sets a visually hidden title (Command Palette) and description. Override with title and description.
Collections
Items live in an Ark collection, not a loose items prop on the root.
const { contains } = useFilter({ sensitivity: "base" });
const { collection, filter } = useListCollection({
initialItems,
filter: contains,
});
<Command
collection={collection}
onInputValueChange={({ inputValue }) => filter(inputValue)}
>Call filter(inputValue) from onInputValueChange. Render collection.items (or collection.group() when grouping).
Keys in grouped lists
When a separator sits beside a group, wrap both in a fragment and put key on the fragment — not only on CommandGroup:
{collection.group().map(([group, items], index) => (
<Fragment key={group}>
{index !== 0 && <CommandSeparator />}
<CommandGroup heading={group}>
{items.map((item) => (
<CommandItem item={item} key={item.value}>
{item.label}
</CommandItem>
))}
</CommandGroup>
</Fragment>
))}API Reference
shadcn.io Command wraps Combobox (Ark Combobox machine). Defaults below are shadcn.io Command values. data-scope stays combobox; shadcn.io sets data-slot="command" on the root.
asChild merges props onto a single child element.
Command
Root. Renders a div. Requires a collection. The list is open by default.
| Prop | Type | Default | Description |
|---|---|---|---|
collection | ListCollection<T> | required | Items to show. Create with useListCollection or createListCollection. |
allowCustomValue | boolean | false | Allow values that are not in the collection. |
alwaysSubmitOnEnter | boolean | false | Submit on Enter even if the list is open. |
asChild | boolean | false | Render the child element instead of a div. |
autoFocus | boolean | - | Focus the input on mount. Prefer autoFocus on CommandInput (already true there). |
className | string | - | Class names on the root. |
closeOnSelect | boolean | false | Close the list when an item is selected. Command keeps the list open. |
composite | boolean | true | Treat as composed with other composite widgets such as tabs. |
defaultHighlightedValue | string | null | - | Uncontrolled initial highlighted value. |
defaultInputValue | string | "" | Uncontrolled initial input text. |
defaultOpen | boolean | - | Uncontrolled initial open state. Command sets open to true. |
defaultValue | string[] | [] | Uncontrolled selected values. |
disabled | boolean | - | Disable the command. |
disableLayer | boolean | true | Do not register as a dismissable layer. The dialog is the layer when used in CommandDialog. |
form | string | - | Associated form id. |
hideMode | "display-none" | "activity" | "display-none" | How to hide mounted-but-closed content. activity needs React 19+. |
highlightedValue | string | null | - | Controlled highlighted value. |
id | string | - | Unique id for the machine. |
ids | Partial<{ root: string; label: string; control: string; input: string; content: string; trigger: string; clearTrigger: string; item: (id: string, index?: number) => string; positioner: string; itemGroup: (id: string | number) => string; itemGroupLabel: (id: string | number) => string }> | - | Element ids for composition. |
immediate | boolean | - | Apply presence changes immediately instead of the next frame. |
inputBehavior | "none" | "autocomplete" | "autohighlight" | "autohighlight" | Highlights the first match while typing. |
inputValue | string | - | Controlled input text. |
invalid | boolean | - | Mark as invalid. |
lazyMount | boolean | true | Mount content on first open. |
loopFocus | boolean | false | Do not loop keyboard focus through items. |
multiple | boolean | - | Allow more than one selected value. |
name | string | - | name on the input for form submission. |
navigate | (details: NavigateDetails) => void | - | Called when a link item is chosen. { value, node, href }. |
onExitComplete | () => void | - | Called when the close animation finishes. |
onFocusOutside | (event: FocusOutsideEvent) => void | - | Called when focus moves outside. |
onHighlightChange | (details: HighlightChangeDetails<T>) => void | - | Called when the highlighted item changes. { highlightedValue, highlightedItem }. |
onInputValueChange | (details: InputValueChangeDetails) => void | - | Called when the input text changes. { inputValue, reason }. Filter the collection here. |
onInteractOutside | (event: InteractOutsideEvent) => void | - | Called on outside interaction. |
onOpenChange | (details: OpenChangeDetails) => void | - | Called when open state changes. { open, reason, value }. |
onPointerDownOutside | (event: PointerDownOutsideEvent) => void | - | Called on pointer down outside. |
onSelect | (details: SelectionDetails) => void | - | Called when an item is selected. { value, itemValue }. |
onValueChange | (details: ValueChangeDetails<T>) => void | - | Called when the selection changes. { value: string[]; items: T[] }. Close a dialog from here. |
open | boolean | true | Controlled open state. Command keeps the list open unless you override this. |
openOnChange | boolean | ((details: InputValueChangeDetails) => boolean) | true | Open the list when the input value changes. |
openOnClick | boolean | true | Open the list on click in the input. |
openOnKeyPress | boolean | true | Open the list on arrow keys. |
placeholder | string | - | Placeholder on the root. Prefer placeholder on CommandInput. |
positioning | PositioningOptions | { placement: "bottom-start" } | Floating position. Unused while CommandContent is inline. |
present | boolean | - | Controlled presence. |
readOnly | boolean | - | Non-editable, but still interactive. |
required | boolean | - | Mark as required. |
scrollToIndexFn | (details: ScrollToIndexDetails) => void | - | Scroll a virtualized list to an index. { index, immediate, getElement }. |
selectionBehavior | "clear" | "replace" | "preserve" | "clear" | After select, clear the input. |
skipAnimationOnMount | boolean | false | Skip the initial presence animation. |
translations | IntlTranslations | - | { triggerLabel?, clearTriggerLabel? } for assistive labels. |
unmountOnExit | boolean | true | Unmount after the close animation. |
value | string[] | - | Controlled selected values. |
| Attribute | Description |
|---|---|
data-slot | command |
data-scope | combobox |
data-part | root |
data-invalid | Present when invalid |
data-readonly | Present when read-only |
CommandDialog
Dialog root for a palette overlay. Same API as Dialog (open, onOpenChange, lazyMount, modal, …). shadcn.io defaults lazyMount and unmountOnExit to true.
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | - | Controlled open state. |
defaultOpen | boolean | false | Uncontrolled initial open state. |
onOpenChange | (details: { open: boolean }) => void | - | Called when the dialog opens or closes. |
modal | boolean | true | Trap pointer events and hide content behind the dialog. |
lazyMount | boolean | true | Mount content on first open. |
unmountOnExit | boolean | true | Unmount content after the close animation. |
See Dialog for the full root API.
CommandDialogTrigger
Opens the palette. Same as DialogTrigger. Renders a button.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child (for example Button). |
className | string | - | Class names on the trigger. |
| Attribute | Description |
|---|---|
data-slot | command-dialog-trigger |
data-scope | dialog |
data-part | trigger |
data-state | "open" or "closed" |
CommandDialogContent
Portaled dialog panel. Includes overlay and positioner. Renders a visually hidden DialogHeader (title + description). No close button — dismiss with Escape, outside click, or onValueChange.
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl" | "6xl" | "fullscreen" | "lg" | Max width of the panel. lg is max-w-xl. |
title | string | "Command Palette" | Accessible dialog title (visually hidden). |
description | string | "Search for a command to run..." | Accessible dialog description (visually hidden). |
bottomStickOnMobile | boolean | - | Stick the panel to the bottom of the viewport below sm. |
positionerClassName | string | - | Class names on the viewport-fixed positioner. |
className | string | - | Class names on the panel. Padding and border are removed (p-0 border-0). |
asChild | boolean | false | Merge onto a single child of the panel. |
showCloseButton is accepted (from DialogContent) but ignored.
| Attribute | Description |
|---|---|
data-slot | command-dialog-content |
data-scope | dialog |
data-part | content |
data-state | "open" or "closed" |
CommandInput
Search field with a decorative search icon. Uses Input Group. autoFocus is on by default. aria-label falls back to placeholder, then "Search".
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | "md" | Control height. sm is h-7, md is h-8, lg is h-9. |
placeholder | string | - | Placeholder on the input. Also used as aria-label when none is set. |
disabled | boolean | - | Disable the input. |
className | string | - | Class names on the input group. |
asChild | boolean | false | Merge onto the underlying input. |
Native input attributes pass through to the input.
| Attribute | Description |
|---|---|
data-slot | command-input on the input |
data-scope | combobox |
data-part | input |
data-invalid | Present when invalid |
data-state | "open" or "closed" |
The control wrapper keeps Combobox slots (combobox-control).
CommandContent
Inline list surface (not portaled). Renders a div. Sits under the input; pair with CommandFooter after it in the tree.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the content. |
| Attribute | Description |
|---|---|
data-slot | command-content |
data-scope | combobox |
data-part | content |
data-state | "open" or "closed" |
data-empty | Present when there are no items |
| CSS variable | Description |
|---|---|
--available-height | Used as max-h-(--available-height) |
--layer-index | Index in the dismissable layer stack |
--nested-layer-count | Number of nested comboboxes |
CommandList
Wraps the options. Outer wrapper is max-h-72. Renders a div.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the list. |
| Attribute | Description |
|---|---|
data-slot | command-list |
data-scope | combobox |
data-part | list |
data-empty | Present when there are no items |
CommandEmpty
Shown when the filtered collection has no items. Default children: No results found.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the empty state. |
children | ReactNode | No results found. | Empty message. |
| Attribute | Description |
|---|---|
data-slot | command-empty |
data-scope | combobox |
data-part | empty |
CommandGroup
Groups related options. Pass heading to render CommandGroupLabel.
| Prop | Type | Default | Description |
|---|---|---|---|
heading | string | ReactNode | - | Group label. |
id | string | - | Group id. |
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the group. |
| Attribute | Description |
|---|---|
data-slot | command-group |
data-scope | combobox |
data-part | item-group |
data-empty | Present when the group is empty |
CommandGroupLabel
Label for a group. Usually created by heading on CommandGroup. Renders a div.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the label. |
| Attribute | Description |
|---|---|
data-slot | command-group-label |
data-scope | combobox |
data-part | item-group-label |
CommandItem
A single command. Always uses persistFocus. No check indicator (unlike Combobox). Renders a div.
| Prop | Type | Default | Description |
|---|---|---|---|
item | T | required | Collection item. |
asChild | boolean | false | Merge onto a single child (for example an <a>). |
className | string | - | Class names on the item. |
persistFocus | boolean | true | Keep highlight when the pointer leaves. |
| Attribute | Description |
|---|---|
data-slot | command-item |
data-scope | combobox |
data-part | item |
data-highlighted | Present when highlighted |
data-state | "checked" or "unchecked" |
data-disabled | Present when disabled |
data-value | The item value |
CommandSeparator
Separator between groups or items.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Class names on the separator. |
| Attribute | Description |
|---|---|
data-slot | command-separator |
CommandShortcut
Keyboard shortcut hint. Renders a span aligned to the end of the item (Menu shortcut styles).
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Class names on the shortcut. |
asChild | boolean | false | Merge onto a single child. |
| Attribute | Description |
|---|---|
data-slot | command-shortcut |
CommandFooter
Footer for hints or extra actions. Place after CommandContent. Renders a div.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Class names on the footer. |
| Attribute | Description |
|---|---|
data-slot | command-footer |
CommandRootProvider
Root alternative that takes the API from useCommand. Renders a div with the same chrome as Command.
| Prop | Type | Default | Description |
|---|---|---|---|
value | UseComboboxReturn<T> | required | Return value of useCommand(). |
asChild | boolean | false | Render the child element instead of a div. |
className | string | - | Class names on the root. |
hideMode | "display-none" | "activity" | "display-none" | How to hide mounted-but-closed content. |
immediate | boolean | - | Apply presence changes immediately. |
lazyMount | boolean | true | Mount content on first open. |
onExitComplete | () => void | - | Called when the close animation finishes. |
present | boolean | - | Controlled presence. |
skipAnimationOnMount | boolean | false | Skip the initial presence animation. |
unmountOnExit | boolean | true | Unmount after the close animation. |
Pass collection and handlers to useCommand(), not to CommandRootProvider.
| Attribute | Description |
|---|---|
data-slot | command |
data-scope | combobox |
data-part | root |
useCommand
Creates the command API for CommandRootProvider. Same options as Command, with palette defaults already applied (open, inputBehavior, selectionBehavior, closeOnSelect, disableLayer, loopFocus).
const command = useCommand({
collection,
onInputValueChange: (details) => filter(details.inputValue),
});
command.focus();CommandContext / useCommandContext
Render-prop or hook access to command state. Use inside Command or CommandRootProvider.
| Property | Type | Description |
|---|---|---|
focused | boolean | Whether the input is focused. |
open | boolean | Whether the list is open. |
inputValue | string | Current input text. |
highlightedValue | string | null | Value of the highlighted item. |
highlightedItem | T | null | Highlighted item. |
setHighlightValue | (value: string) => void | Highlight an item by value. |
clearHighlightValue | () => void | Clear the highlight. |
syncSelectedItems | () => void | Sync selected items after an async collection load. |
selectedItems | T[] | Selected items. |
hasSelectedItems | boolean | Whether any item is selected. |
value | string[] | Selected values. |
valueAsString | string | Selected items as a string. |
selectValue | (value: string) => void | Select a value. |
setValue | (value: string[]) => void | Set the selection. |
clearValue | (value?: string) => void | Clear one value, or all if omitted. |
focus | () => void | Focus the input. |
setInputValue | (value: string, reason?: InputValueChangeReason) => void | Set the input text. |
getItemState | (props: { item: T; persistFocus?: boolean }) => ItemState | State for one item (value, disabled, selected, highlighted). |
setOpen | (open: boolean, reason?: OpenChangeReason) => void | Open or close the list. |
collection | ListCollection<T> | Current collection. |
reposition | (options?: Partial<PositioningOptions>) => void | Update list position. |
multiple | boolean | Whether multiple selection is on. |
disabled | boolean | Whether the command is disabled. |
CommandContext children: (context) => ReactNode.
CommandItemContext / useCommandItemContext
Render-prop or hook access to one item. Use inside CommandItem.
| Property | Type | Description |
|---|---|---|
value | string | Item value. |
disabled | boolean | Whether the item is disabled. |
selected | boolean | Whether the item is selected. |
highlighted | boolean | Whether the item is highlighted. |
CommandItemContext children: (context) => ReactNode.
Accessibility
Complies with the Combobox WAI-ARIA design pattern. CommandInput labels itself from aria-label, then placeholder, then "Search". Dialog palettes also expose CommandDialogContent title / description to assistive tech (visually hidden). Keep CommandEmpty so a filtered list is not a blank panel.
Keyboard support
| Key | Description |
|---|---|
ArrowDown | Moves to the next option. Opens the list if it was closed. |
ArrowUp | Moves to the previous option. Opens the list if it was closed. |
Home | Moves to the first option. |
End | Moves to the last option. |
Enter | Runs the highlighted command. Clears the input (selectionBehavior="clear"). |
Escape | Clears highlight / input. In CommandDialog, also closes the dialog. |