---
layout: ~/layouts/MdxDocLayout.astro
title: "CommandPalette"
description: "A composable command palette component for search and command dialogs."
sourceFile: "components/command-palette"
---

import ComponentExample from "~/components/docs/ComponentExample.astro";
import ComponentSection from "~/components/docs/ComponentSection.astro";
import Heading from "~/components/docs/Heading.astro";
import KeyboardShortcuts from "~/components/docs/KeyboardShortcuts.astro";
import PropsTable from "~/components/docs/PropsTable.astro";
import {
  CommandPaletteBasicDemo,
  CommandPaletteSimpleDemo,
  CommandPaletteLoadingDemo,
  CommandPaletteResultItemDemo,
} from "~/components/demos/CommandPaletteDemo";

{/* Hero Demo */}

<ComponentSection>
  <ComponentExample demo="CommandPaletteBasicDemo">
    <CommandPaletteBasicDemo client:visible />
  </ComponentExample>
</ComponentSection>

{/* Installation */}

<ComponentSection>
  <Heading level={2}>Installation</Heading>
  <Heading level={3}>Barrel</Heading>

```tsx
import { CommandPalette } from "@cloudflare/kumo";
```

<Heading level={3}>Granular</Heading>

```tsx
import { CommandPalette } from "@cloudflare/kumo/components/command-palette";
```

</ComponentSection>

{/* Usage */}

<ComponentSection>
  <Heading level={2}>Usage</Heading>
  <p>
    CommandPalette is a compound component built on Base UI's Autocomplete
    primitive. It provides accessible keyboard navigation and customizable
    styling for command palette interfaces.
  </p>

```tsx
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>
    </>
  );
}
```

</ComponentSection>

{/* Keyboard Navigation */}

<ComponentSection>
  <Heading level={2}>Keyboard Navigation</Heading>
  <p>Built-in keyboard navigation is provided automatically:</p>
  <KeyboardShortcuts
    shortcuts={[
      { keys: ["↑", "↓"], description: "Move highlight between items" },
      { keys: ["Enter"], description: "Select highlighted item" },
      { keys: ["⌘/Ctrl", "Enter"], description: "Select with newTab: true" },
      { keys: ["Escape"], description: "Close the dialog" },
    ]}
  />
</ComponentSection>

{/* Examples */}

<ComponentSection>
  <Heading level={2}>Examples</Heading>

<Heading level={3}>With Grouped Items</Heading>
<p>Group related commands together with labels.</p>
<ComponentExample demo="CommandPaletteBasicDemo">
  <CommandPaletteBasicDemo client:visible />
</ComponentExample>

<Heading level={3}>Simple Flat List</Heading>
<p>For simpler use cases, use a flat array of items without grouping.</p>
<ComponentExample demo="CommandPaletteSimpleDemo">
  <CommandPaletteSimpleDemo client:visible />
</ComponentExample>

<Heading level={3}>Loading State</Heading>
<p>Show a loading spinner while fetching results.</p>
<ComponentExample demo="CommandPaletteLoadingDemo">
  <CommandPaletteLoadingDemo client:visible />
</ComponentExample>

  <Heading level={3}>ResultItem with Breadcrumbs</Heading>
  <p>
    Use `ResultItem` for rich items with
    breadcrumbs, icons, and optional text highlighting.
  </p>
  <ComponentExample demo="CommandPaletteResultItemDemo">
    <CommandPaletteResultItemDemo client:visible />
  </ComponentExample>
</ComponentSection>

{/* Component Parts */}

<ComponentSection>
  <Heading level={2}>Component Parts</Heading>

<Heading level={3}>CommandPalette.Root</Heading>
<p>
  The main wrapper that combines Dialog + Panel. Manages open state and
  Autocomplete functionality.
</p>

<Heading level={3}>CommandPalette.Dialog</Heading>
<p>
  Modal dialog wrapper. Use with Panel for swappable content (e.g., drill-down
  navigation).
</p>

<Heading level={3}>CommandPalette.Panel</Heading>
<p>
  Autocomplete panel without dialog. Use inside Dialog for content that can swap
  without re-mounting.
</p>

<Heading level={3}>CommandPalette.Input</Heading>
<p>Search input field with auto-focus and keyboard handling.</p>

<Heading level={3}>CommandPalette.List</Heading>
<p>Scrollable container for results.</p>

<Heading level={3}>CommandPalette.Results</Heading>
<p>Render prop iterator for items/groups.</p>

<Heading level={3}>CommandPalette.Group</Heading>
<p>Category grouping container.</p>

<Heading level={3}>CommandPalette.GroupLabel</Heading>
<p>Section header text within a group.</p>

<Heading level={3}>CommandPalette.Items</Heading>
<p>Render prop iterator for items within a group.</p>

<Heading level={3}>CommandPalette.Item</Heading>
<p>Basic selectable item.</p>

<Heading level={3}>CommandPalette.ResultItem</Heading>
<p>Rich item with breadcrumbs, icons, and text highlighting.</p>

<Heading level={3}>CommandPalette.HighlightedText</Heading>
<p>Renders text with highlighted portions based on match indices.</p>

<Heading level={3}>CommandPalette.Empty</Heading>
<p>Empty state when no results match.</p>

<Heading level={3}>CommandPalette.Loading</Heading>
<p>Loading spinner state.</p>

  <Heading level={3}>CommandPalette.Footer</Heading>
  <p>
    Footer for keyboard hints or other content.
  </p>
</ComponentSection>

{/* API Reference */}

<ComponentSection>
  <Heading level={2}>API Reference</Heading>

<Heading level={3}>CommandPalette.Root Props</Heading>

```tsx
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;
}
```

<Heading level={3}>CommandPalette.ResultItem Props</Heading>

```tsx
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;
}
```

</ComponentSection>
