Command
A filterable command palette, inline or in a dialog.
A palette runs commands, so why is it a combobox? Because the role follows the interaction, not the payload: type to filter, arrow to highlight, enter to run — and a menu cannot filter at all. See the rule.
Usage
import {
Command,
CommandContent,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandShortcut,
useFilter,
useListCollection,
} from "@kanzo-tech/ui";Command is a Combobox that is permanently open, never closes on select, and clears its
selection after each pick — the behaviours a palette needs and a combobox does not. You drive
it with a list collection and filter its items on every keystroke:
const { contains } = useFilter({ sensitivity: "base" });
const { collection, filter } = useListCollection({
initialItems: COMMANDS,
filter: contains,
groupBy: (item) => item.group,
});Filtering happens in your collection, not inside the component, so the same palette can be fed by a local array or a server search without changing its markup.
Anatomy
<Command collection>
<CommandInput />
<CommandContent>
<CommandEmpty />
<CommandList>
<CommandGroup heading>
<CommandItem item>
<CommandShortcut />
<CommandSeparator />
<CommandFooter />The root is flex-1 with min-h-0, so it needs a parent that gives it a height — a flex
column of fixed height inline, or CommandDialogContent.
In a dialog
CommandDialog is the Dialog root re-exported, and CommandDialogContent supplies the
sizing plus a screen-reader-only title and description, so the palette is announced properly
without visible chrome. Because the root is Dialog's, so is the trigger: use DialogTrigger
for an uncontrolled palette, or drive open yourself when a ⌘K handler shares the state, as
below.
Keyboard
| Key | Does |
|---|---|
| ↓ / ↑ | Moves the highlight to the next / previous command, stopping at the ends rather than wrapping. |
| Home / End | Highlights the first / last command — the list owns them outright, because the palette is never closed. |
| Enter | Runs the highlighted command; the list stays open and the query clears. |
| Esc | Closes CommandDialog; an inline palette has nothing to close. |
Command sets inputBehavior="autohighlight", so the top match is already highlighted after
every keystroke and Enter runs it without a single ↓; no global shortcut is
bound for you — ⌘+K is yours to wire.
API Reference
CommandDialogContent
| Prop | Type | Default |
|---|---|---|
title | string | "Command Palette" |
description | string | "Search for a command to run..." |
size | DialogContentProps["size"] | "lg" |
CommandInput
| Prop | Type | Default |
|---|---|---|
size | InputProps["size"] | "md" |
CommandGroup
| Prop | Type | Default |
|---|---|---|
heading | string | ReactNode | — |
collection, onInputValueChange, onValueChange and the rest are Ark's Combobox.Root;
CommandItem takes Ark's item.