<CommandPalette.Root
open={open}
onOpenChange={setOpen}
items={groups}
value={search}
onValueChange={setSearch}
>
<CommandPalette.Input placeholder="Search..." />
<CommandPalette.List>
<CommandPalette.Results>
{(group) => (
<CommandPalette.Group>
<CommandPalette.GroupLabel>{group.label}</CommandPalette.GroupLabel>
<CommandPalette.Items>
{(item) => (
<CommandPalette.Item value={item}>
{item.title}
</CommandPalette.Item>
)}
</CommandPalette.Items>
</CommandPalette.Group>
)}
</CommandPalette.Results>
<CommandPalette.Empty>No results</CommandPalette.Empty>
</CommandPalette.List>
<CommandPalette.Footer>...</CommandPalette.Footer>
</CommandPalette.Root> Installation
Barrel
import { CommandPalette } from "@cloudflare/kumo"; Granular
import { CommandPalette } from "@cloudflare/kumo/components/command-palette"; Usage
CommandPalette is a compound component built on Base UI's Autocomplete primitive. It provides accessible keyboard navigation and customizable styling for command palette interfaces.
import { useState } from "react";
import { CommandPalette } from "@cloudflare/kumo";
interface Item {
id: string;
title: string;
}
const items: Item[] = [
{ id: "1", title: "Create Project" },
{ id: "2", title: "Open Settings" },
];
export default function Example() {
const [open, setOpen] = useState(false);
const [search, setSearch] = useState("");
return (
<>
<button onClick={() => setOpen(true)}>Open</button>
<CommandPalette.Root
open={open}
onOpenChange={setOpen}
items={items}
value={search}
onValueChange={setSearch}
itemToStringValue={(item) => item.title}
getSelectableItems={(items) => items}
>
<CommandPalette.Input placeholder="Search..." />
<CommandPalette.List>
<CommandPalette.Results>
{(item) => (
<CommandPalette.Item
key={item.id}
value={item}
onClick={() => setOpen(false)}
>
{item.title}
</CommandPalette.Item>
)}
</CommandPalette.Results>
<CommandPalette.Empty>No results</CommandPalette.Empty>
</CommandPalette.List>
</CommandPalette.Root>
</>
);
} Keyboard Navigation
Built-in keyboard navigation is provided automatically:
- ↑ ↓ - Move highlight between items
- Enter - Select highlighted item
- ⌘/Ctrl + Enter - Select with
newTab: true - Escape - Close the dialog
Examples
With Grouped Items
Group related commands together with labels.
<CommandPalette.Root items={groups} ...>
<CommandPalette.Input placeholder="Type a command..." />
<CommandPalette.List>
<CommandPalette.Results>
{(group) => (
<CommandPalette.Group>
<CommandPalette.GroupLabel>{group.label}</CommandPalette.GroupLabel>
<CommandPalette.Items>
{(item) => (
<CommandPalette.Item value={item}>
{item.title}
</CommandPalette.Item>
)}
</CommandPalette.Items>
</CommandPalette.Group>
)}
</CommandPalette.Results>
</CommandPalette.List>
</CommandPalette.Root> Simple Flat List
For simpler use cases, use a flat array of items without grouping.
<CommandPalette.Root
items={items}
itemToStringValue={(item) => item.title}
getSelectableItems={(items) => items}
>
<CommandPalette.Input placeholder="Search actions..." />
<CommandPalette.List>
<CommandPalette.Results>
{(item) => (
<CommandPalette.Item value={item}>
{item.title}
</CommandPalette.Item>
)}
</CommandPalette.Results>
</CommandPalette.List>
</CommandPalette.Root> Loading State
Show a loading spinner while fetching results.
<CommandPalette.List>
{loading ? (
<CommandPalette.Loading />
) : (
<CommandPalette.Results>
{(item) => ...}
</CommandPalette.Results>
)}
</CommandPalette.List> ResultItem with Breadcrumbs
Use ResultItem for rich items with
breadcrumbs, icons, and optional text highlighting.
<CommandPalette.ResultItem
value={item}
title={item.title}
breadcrumbs={["Components"]}
icon={<FileIcon size={16} />}
onClick={(e) => handleClick(e)}
/> Component Parts
CommandPalette.Root
The main wrapper that combines Dialog + Panel. Manages open state and Autocomplete functionality.
CommandPalette.Dialog
Modal dialog wrapper. Use with Panel for swappable content (e.g., drill-down navigation).
CommandPalette.Panel
Autocomplete panel without dialog. Use inside Dialog for content that can swap without re-mounting.
CommandPalette.Input
Search input field with auto-focus and keyboard handling.
CommandPalette.List
Scrollable container for results.
CommandPalette.Results
Render prop iterator for items/groups.
CommandPalette.Group
Category grouping container.
CommandPalette.GroupLabel
Section header text within a group.
CommandPalette.Items
Render prop iterator for items within a group.
CommandPalette.Item
Basic selectable item.
CommandPalette.ResultItem
Rich item with breadcrumbs, icons, and text highlighting.
CommandPalette.HighlightedText
Renders text with highlighted portions based on match indices.
CommandPalette.Empty
Empty state when no results match.
CommandPalette.Loading
Loading spinner state.
CommandPalette.Footer
Footer for keyboard hints or other content.
API Reference
CommandPalette.Root Props
interface CommandPaletteRootProps<TGroup, TItem> {
// Dialog state
open: boolean;
onOpenChange: (open: boolean) => void;
onBackdropClick?: (e: React.MouseEvent) => void;
// Autocomplete
items: TGroup[];
value?: string;
onValueChange?: (value: string) => void;
itemToStringValue?: (item: TGroup) => string;
filter?: (item: TGroup, query: string) => boolean;
onItemHighlighted?: (item: TGroup | undefined, details: {...}) => void;
// Selection
onSelect?: (item: TItem, options: { newTab: boolean }) => void;
getSelectableItems?: (items: TGroup[]) => TItem[];
children: React.ReactNode;
} CommandPalette.ResultItem Props
interface CommandPaletteResultItemProps<T> {
value: T;
title: string;
breadcrumbs?: string[];
titleHighlights?: [number, number][];
breadcrumbHighlights?: [number, number][][];
description?: string;
icon?: React.ReactNode;
onClick: (e: React.MouseEvent) => void;
showArrow?: boolean; // default: true
external?: boolean; // shows external link icon
nonInteractive?: boolean;
}