Command
Command palette / ⌘K menu — filterable, keyboard-driven action launcher with inline and modal modes, grouped items, descriptions, breadcrumbs, shortcuts, and footer key hints.
import {
Command,
useCommandShortcut,
} from "@tessinaui/ui";
Embed the palette directly in the page. The list is always visible — typing into the input filters items live.
<Command.Root size="md" rounded="xl">
<Command.Input placeholder="Type a command or search…" shortcut="⌘K" />
<Command.List>
<Command.Empty>No results found.</Command.Empty>
<Command.Group heading="Suggestions">
<Command.Item value="dashboard" leadingIcon={<LayoutDashboard />}>
<Command.ItemTitle>Dashboard</Command.ItemTitle>
<Command.Shortcut>⌘D</Command.Shortcut>
</Command.Item>
<Command.Item value="settings" leadingIcon={<Settings />}>
<Command.ItemTitle>Settings</Command.ItemTitle>
</Command.Item>
</Command.Group>
</Command.List>
</Command.Root>
Wrap the same content in Command.Dialog to make a ⌘K overlay. Pair with useCommandShortcut("k", …) for a global keyboard trigger.
function App() {
const [open, setOpen] = useState(false);
useCommandShortcut("k", () => setOpen((o) => !o));
return (
<Command.Dialog open={open} onOpenChange={setOpen}>
<Command.Input placeholder="Search…" />
<Command.List>
<Command.Empty>No results found.</Command.Empty>
<Command.Group heading="Actions">
<Command.Item value="new-file" onSelect={() => createFile()}>
<Command.ItemTitle>New file</Command.ItemTitle>
<Command.Shortcut>⌘N</Command.Shortcut>
</Command.Item>
</Command.Group>
</Command.List>
<Command.Footer>
<Command.FooterHint shortcut="enter">To select</Command.FooterHint>
<Command.FooterHint shortcut="arrows">To navigate</Command.FooterHint>
<Command.FooterHint shortcut="escape" appearance="ghost">To close</Command.FooterHint>
</Command.Footer>
</Command.Dialog>
);
}
Items can hold a title, optional description, breadcrumb path, and a trailing shortcut.
<Command.Item value="settings-team" leadingIcon={<Users />}>
<Command.ItemTitle>Team settings</Command.ItemTitle>
<Command.ItemDescription>Invite or remove members</Command.ItemDescription>
<Command.Shortcut>⌘T</Command.Shortcut>
</Command.Item>
<Command.Item value="docs-deep-link">
<Command.Crumbs crumbs={["Components", "Form", "Field"]} />
</Command.Item>
Both accept the same visual props. Command.Root renders inline; Command.Dialog renders inside a Base UI Dialog with backdrop and focus trap.
| Prop | Type | Default | Description |
|---|
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | Input height, padding, and typography scale |
rounded | "none" | "sm" | "md" | "lg" | "xl" | "full" | "full" (Dialog) / "xl" (Root) | Container border radius (full = 36px) |
width | "narrow" | "default" | "wide" | "default" | Max-width — used when modal |
intent | "none" | "error" | "warning" | "success" | "info" | "none" | Semantic accent on the container border |
dir | "ltr" | "rtl" | "ltr" | Text direction. Flips chevrons and shortcut alignment |
value / defaultValue | Value | — | Controlled / uncontrolled selected item value (from Base UI Combobox) |
onValueChange | (value) => void | — | Fires when the selection changes |
inputValue / defaultInputValue | string | — | Controlled / uncontrolled text input value |
onInputValueChange | (value) => void | — | Fires as the user types |
disabled | boolean | false | Disables all interaction |
| Prop | Type | Default | Description |
|---|
open | boolean | — | Controlled open state |
defaultOpen | boolean | — | Uncontrolled initial open state |
onOpenChange | (open: boolean, event: Event) => void | — | Fires when the dialog opens or closes |
dismissible | boolean | true | Whether Escape / backdrop click closes the dialog |
modal | boolean | true | Whether focus is trapped and page scroll is locked |
label | string | "Command palette" | a11y label for the Dialog popup |
| Prop | Type | Default | Description |
|---|
placeholder | string | "Type a command or search…" | Placeholder text |
leadingIcon | ReactNode | search icon | Override the leading icon. Pass null to hide |
shortcut | ReactNode | — | Trailing kbd hint shown to the right of the input |
trailing | ReactNode | — | Additional trailing content (e.g. mic button) |
showClear | boolean | true | Show the × clear button when the input has a value |
| Prop | Type | Default | Description |
|---|
value | string | — | Value used for filtering and selection |
leadingIcon | ReactNode | — | Leading slot — typically a Lucide icon |
leadingAvatar | ReactNode | — | Leading slot — avatar visual (mutually exclusive with leadingIcon) |
trailingIcon | ReactNode | — | Trailing slot — icon or status indicator |
hideLeading | boolean | false | Hide the leading slot entirely |
onSelect | (value) => void | — | Fires when the item is selected (click or Enter) |
disabled | boolean | false | Disables the item |
| Prop | Type | Default | Description |
|---|
heading | ReactNode | — | Group heading rendered above the items |
headingTrailing | ReactNode | — | Optional slot rendered at the end of the heading row (e.g. "See all" link) |
| Prop | Type | Default | Description |
|---|
crumbs | ReactNode[] | — | Path segments. Last segment styled as the destination |
separator | ReactNode | chevron right | Custom separator between segments |
| Prop | Type | Default | Description |
|---|
icon | ReactNode | — | Optional decorative icon shown above the empty text |
| Prop | Type | Default | Description |
|---|
label | ReactNode | "Loading…" | Loading message rendered beside the spinner |
| Prop | Type | Default | Description |
|---|
shortcut | ReactNode | "enter" | "arrow-up" | "arrow-down" | "arrows" | "escape" | "tab" | "shift" | — | Glyph or preset for the kbd chip |
labelPosition | "before" | "after" | "after" | Whether the label sits before or after the chip |
appearance | "filled" | "ghost" | "filled" | Filled (default) or outlined chip |
For Mobbin / Linear / Raycast-style palettes, compose additional parts on top of the core API:
Command.Chips + Command.ChipItem — horizontal scrollable pill row (recent searches, filters)
Command.Body + Command.Sidebar + Command.SidebarItem — two-column layout with an inner view-switching nav rail
Command.Section — heading wrapper for arbitrary content; auto-hides when its filterable children are all filtered out
Command.Grid + Command.Card — thumbnail card grid (filter-aware via value)
Command.IconRow + Command.AppIcon — horizontal app logo strip (decorative, non-filterable)
<Command.Dialog>
<Command.Input
placeholder="iOS Apps, Screens, UI Elements, Flows or Keywords…"
trailing={
<SegmentedControl size="xs" value={platform} onValueChange={setPlatform}>
<SegmentedControlItem value="ios"><Smartphone /></SegmentedControlItem>
<SegmentedControlItem value="desktop"><Monitor /></SegmentedControlItem>
</SegmentedControl>
}
/>
<Command.Chips>
<Command.ChipItem leadingIcon={<Search />} value="transactions">transactions</Command.ChipItem>
<Command.ChipItem leadingIcon={<DocFile />} value="note-detail">Note Detail</Command.ChipItem>
</Command.Chips>
<Command.Body>
<Command.Sidebar value={view} onValueChange={setView}>
<Command.SidebarItem value="trending" leadingIcon={<TrendingUp />}>Trending</Command.SidebarItem>
<Command.SidebarItem value="screens" leadingIcon={<Smartphone />}>Screens</Command.SidebarItem>
</Command.Sidebar>
<Command.List>
<Command.IconRow>
<Command.AppIcon src="/duolingo.png" label="Duolingo" />
<Command.AppIcon src="/wise.png" label="Wise" />
</Command.IconRow>
<Command.Section heading="Screens">
<Command.Grid columns={3}>
<Command.Card value="signup" title="Signup" image="/signup.png" />
<Command.Card value="login" title="Login" image="/login.png" />
</Command.Grid>
</Command.Section>
</Command.List>
</Command.Body>
</Command.Dialog>
| Prop | Type | Default | Description |
|---|
label (Chips) | ReactNode | — | Optional label rendered before the pills |
value (ChipItem) | string | — | Filter value. Chip hides when the input query doesn't match |
leadingIcon | ReactNode | — | Leading icon |
leadingAvatar | ReactNode | — | Leading avatar (replaces icon) |
onRemove | () => void | — | Show a trailing × button; called when clicked |
| Prop | Type | Default | Description |
|---|
minHeight (Body) | string | number | "320px" | Min-height of the row layout |
value (Sidebar) | string | — | Controlled active item value |
defaultValue (Sidebar) | string | — | Uncontrolled initial active value |
onValueChange (Sidebar) | (value) => void | — | Fires when active item changes |
value (SidebarItem) | string | — | Item identifier, compared against the sidebar's value |
leadingIcon / trailingIcon (SidebarItem) | ReactNode | — | Icons |
| Prop | Type | Default | Description |
|---|
heading | ReactNode | — | Heading rendered above the content |
headingTrailing | ReactNode | — | Slot rendered at the end of the heading row |
Auto-hides when filtering removes every Command.Item, Command.ChipItem, or Command.Card descendant.
| Prop | Type | Default | Description |
|---|
columns (Grid) | 1 | 2 | 3 | 4 | 5 | 6 | 3 | Number of columns |
gap (Grid) | string | "gap-2" | Tailwind gap class |
value (Card) | string | — | Filter value. Card hides on no match |
title / description | ReactNode | — | Text under the media frame |
image (Card) | string | — | Image src (rendered as <img>) |
media (Card) | ReactNode | — | Custom content inside the aspect frame (overrides image) |
aspect (Card) | "square" | "video" | "3/4" | "4/3" | "1/1" | "3/4" | Aspect ratio of the media frame |
onSelect (Card) | (value) => void | — | Called when the card is activated |
| Prop | Type | Default | Description |
|---|
gap (IconRow) | string | "gap-3" | Tailwind gap class |
src (AppIcon) | string | — | Image src for the logo |
label (AppIcon) | string | — | Accessible label / tooltip |
children (AppIcon) | ReactNode | — | Custom content (overrides src) |
Hook for binding a global keyboard trigger.
| Argument | Type | Description |
|---|
key | string | Case-insensitive key name (e.g. "k") |
onTrigger | () => void | Callback fired when the chord is pressed |
options.modifier | "auto" | "ctrl" | "meta" | "alt" | "shift" | "none" | "auto" (default) detects platform: ⌘ on macOS, Ctrl elsewhere |
options.enabled | boolean | Set false to suspend listening |
- The dialog uses
@base-ui/react/dialog — focus is trapped inside, and Escape closes (when dismissible is true).
- The list uses
@base-ui/react/combobox in inline mode — arrow keys move the highlight, Enter selects the highlighted item, typing filters the list.
- Group headings use
<Combobox.GroupLabel> so screen readers announce the section context.
- Empty / loading states are
aria-live announcements via Base UI primitives.
A right-click / long-press menu anchored at the cursor. Built on Base UI's context-menu primitive — supports items, groups, labels, separators, sub-menus, checkbox-items, and radio-items. Compound API mirrors DropdownMenu so you can swap one for the other when the trigger gesture changes from click to right-click.
A floating panel that appears next to its trigger and holds interactive content. Click-activated, with full keyboard support, positioning, an optional arrow, and compound parts for header, body, and footer.