Skip to content

Command

A command palette (⌘K) for quick search and actions. Supports keyboard navigation, grouped items, nested pages, and both data-driven and compound component patterns.

import { Command } from 'asterui'

Basic Usage

Data-driven command palette with grouped items.

import { Command, Button } from 'asterui'
import { useState } from 'react'

function App() {
  const [open, setOpen] = useState(false);
  
  const items = [
    { key: 'new-file', label: 'Create New File', group: 'Actions' },
    { key: 'new-folder', label: 'Create New Folder', group: 'Actions' },
    { key: 'search', label: 'Search Files', group: 'Actions' },
    { key: 'home', label: 'Go to Home', group: 'Navigation' },
    { key: 'settings', label: 'Open Settings', group: 'Navigation' },
    { key: 'profile', label: 'View Profile', group: 'Navigation' },
  ];
  
  return (
      <>
        <Button color="primary" onClick={() => setOpen(true)}>
          Open Command Palette
        </Button>
        <Command
          open={open}
          onOpenChange={setOpen}
          items={items}
          placeholder="Type a command or search..."
        />
      </>
    )
}

export default App

Compound Components

Build command palettes using compound components for more control.

import { Command, Button } from 'asterui'
import { useState } from 'react'

function App() {
  const [open, setOpen] = useState(false);
  
  return (
      <>
        <Button color="primary" onClick={() => setOpen(true)}>
          Open Command Palette
        </Button>
        <Command open={open} onOpenChange={setOpen}>
          <Command.List>
            <Command.Empty>No results found</Command.Empty>
            <Command.Group heading="Actions">
              <Command.Item onSelect={() => alert('Creating file...')}>
                Create New File
              </Command.Item>
              <Command.Item onSelect={() => alert('Creating folder...')}>
                Create New Folder
              </Command.Item>
            </Command.Group>
            <Command.Separator />
            <Command.Group heading="Navigation">
              <Command.Item onSelect={() => alert('Going home...')}>
                Go to Home
              </Command.Item>
              <Command.Item onSelect={() => alert('Opening settings...')}>
                Open Settings
              </Command.Item>
            </Command.Group>
          </Command.List>
        </Command>
      </>
    )
}

export default App

With Icons

Add icons to command items for visual context.

import { Command, Button } from 'asterui'
import { useState } from 'react'

function App() {
  const [open, setOpen] = useState(false);
  
  const items = [
    { key: 'new-file', label: 'Create New File', group: 'Actions', icon: <FileIcon /> },
    { key: 'new-folder', label: 'Create New Folder', group: 'Actions', icon: <FolderIcon /> },
    { key: 'search', label: 'Search Files', group: 'Actions', icon: <SearchIcon /> },
    { key: 'home', label: 'Go to Home', group: 'Navigation', icon: <HomeIcon /> },
    { key: 'settings', label: 'Open Settings', group: 'Navigation', icon: <SettingsIcon /> },
  ];
  
  return (
      <>
        <Button color="primary" onClick={() => setOpen(true)}>
          Open Command Palette
        </Button>
        <Command
          open={open}
          onOpenChange={setOpen}
          items={items}
          placeholder="Type a command or search..."
        />
      </>
    )
}

export default App

Search Keywords

Add extra keywords to make items findable by alternative terms.

import { Command, Button } from 'asterui'
import { useState } from 'react'

function App() {
  const [open, setOpen] = useState(false);
  
  const items = [
    {
      key: 'new-file',
      label: 'Create New File',
      group: 'Actions',
      keywords: ['add', 'create', 'new', 'file', 'document']
    },
    {
      key: 'search',
      label: 'Search Files',
      group: 'Actions',
      keywords: ['find', 'lookup', 'query']
    },
    {
      key: 'settings',
      label: 'Open Settings',
      group: 'Navigation',
      keywords: ['preferences', 'config', 'options']
    },
    {
      key: 'profile',
      label: 'View Profile',
      group: 'Navigation',
      keywords: ['user', 'account', 'me']
    },
  ];
  
  return (
      <>
        <Button color="primary" onClick={() => setOpen(true)}>
          Open (try searching "preferences")
        </Button>
        <Command
          open={open}
          onOpenChange={setOpen}
          items={items}
          placeholder="Try 'preferences' or 'config'..."
        />
      </>
    )
}

export default App

Nested Pages

Navigate to sub-pages with back navigation support. Press Backspace (when search is empty) or click the back arrow to return.

import { Command, Button } from 'asterui'
import { useState } from 'react'

function App() {
  const [open, setOpen] = useState(false);
  
  return (
      <>
        <Button color="primary" onClick={() => setOpen(true)}>
          Open Command Palette
        </Button>
        <Command open={open} onOpenChange={setOpen}>
          <Command.List>
            <Command.Empty>No results found</Command.Empty>
  
            {/* Root page content */}
            <Command.Group heading="Quick Actions">
              <Command.Item onSelect={() => alert('Creating file...')}>
                Create New File
              </Command.Item>
              <Command.Item onSelect={() => alert('Searching...')}>
                Search Files
              </Command.Item>
            </Command.Group>
            <Command.Separator />
            <Command.Group heading="Navigate">
              <Command.Item value="settings-menu">
                Settings →
              </Command.Item>
              <Command.Item value="theme-menu">
                Theme →
              </Command.Item>
            </Command.Group>
  
            {/* Settings page */}
            <Command.Page id="settings">
              <Command.Group heading="Settings">
                <Command.Item onSelect={() => alert('Opening account settings...')}>
                  Account
                </Command.Item>
                <Command.Item onSelect={() => alert('Opening privacy settings...')}>
                  Privacy
                </Command.Item>
                <Command.Item onSelect={() => alert('Opening notification settings...')}>
                  Notifications
                </Command.Item>
              </Command.Group>
            </Command.Page>
  
            {/* Theme page */}
            <Command.Page id="theme">
              <Command.Group heading="Choose Theme">
                <Command.Item onSelect={() => alert('Theme set to Light')}>
                  Light
                </Command.Item>
                <Command.Item onSelect={() => alert('Theme set to Dark')}>
                  Dark
                </Command.Item>
                <Command.Item onSelect={() => alert('Theme set to System')}>
                  System
                </Command.Item>
              </Command.Group>
            </Command.Page>
          </Command.List>
        </Command>
      </>
    )
}

export default App

Disabled Items

Disable specific items to prevent selection.

import { Command, Button } from 'asterui'
import { useState } from 'react'

function App() {
  const [open, setOpen] = useState(false);
  
  const items = [
    { key: 'new-file', label: 'Create New File', group: 'Actions' },
    { key: 'new-folder', label: 'Create New Folder', group: 'Actions', disabled: true },
    { key: 'search', label: 'Search Files', group: 'Actions' },
    { key: 'delete', label: 'Delete Files', group: 'Danger', disabled: true },
  ];
  
  return (
      <>
        <Button color="primary" onClick={() => setOpen(true)}>
          Open Command Palette
        </Button>
        <Command
          open={open}
          onOpenChange={setOpen}
          items={items}
          placeholder="Some items are disabled..."
        />
      </>
    )
}

export default App

Custom Shortcut

Change the global keyboard shortcut from ⌘K to another key.

import { Command, Button, Space } from 'asterui'
import { useState } from 'react'

function App() {
  const [open1, setOpen1] = useState(false);
  const [open2, setOpen2] = useState(false);
  
  const items = [
    { key: 'action1', label: 'Action 1' },
    { key: 'action2', label: 'Action 2' },
  ];
  
  return (
      <>
        <Space direction="horizontal" size="sm">
          <Button onClick={() => setOpen1(true)}>
            Default (⌘K)
          </Button>
          <Button onClick={() => setOpen2(true)}>
            Custom (⌘J)
          </Button>
        </Space>
        <Command
          open={open1}
          onOpenChange={setOpen1}
          items={items}
          placeholder="Default shortcut ⌘K..."
        />
        <Command
          open={open2}
          onOpenChange={setOpen2}
          items={items}
          shortcut={['j']}
          placeholder="Custom shortcut ⌘J..."
        />
      </>
    )
}

export default App

Custom Filter

Provide a custom filter function for advanced matching logic.

import { Command, Button } from 'asterui'
import { useState } from 'react'

function App() {
  const [open, setOpen] = useState(false);
  
  const items = [
    { key: 'apple', label: 'Apple', keywords: ['fruit', 'red'] },
    { key: 'banana', label: 'Banana', keywords: ['fruit', 'yellow'] },
    { key: 'carrot', label: 'Carrot', keywords: ['vegetable', 'orange'] },
    { key: 'date', label: 'Date', keywords: ['fruit', 'brown'] },
  ];
  
  // Custom filter that only matches from the start of words
  const customFilter = (value: string, search: string, keywords?: string[]) => {
    const searchLower = search.toLowerCase();
    if (value.toLowerCase().startsWith(searchLower)) return true;
    return (keywords || []).some(k => k.toLowerCase().startsWith(searchLower));
  };
  
  return (
      <>
        <Button color="primary" onClick={() => setOpen(true)}>
          Open (prefix matching only)
        </Button>
        <Command
          open={open}
          onOpenChange={setOpen}
          items={items}
          filter={customFilter}
          placeholder="Type 'fr' for fruits..."
        />
      </>
    )
}

export default App
PropertyDescriptionTypeDefault
openControlled open stateboolean-
onOpenChangeCallback when open state changes(open: boolean) => void-
defaultOpenDefault open state (uncontrolled)booleanfalse
itemsData-driven item configurationCommandItemConfig[]-
filterCustom filter function(value: string, search: string, keywords?: string[]) => boolean-
loopLoop keyboard navigation at edgesbooleantrue
shortcutGlobal keyboard shortcut key(s)string[]['k']
placeholderSearch input placeholderstring'Type a command or search...'
emptyMessageMessage when no results foundReactNode'No results found.'
data-testidTest ID for testingstring-

When using the data-driven items prop:

PropertyDescriptionTypeDefault
keyUnique key for the itemstring-
labelDisplay labelReactNode-
groupGroup name for categorizationstring-
keywordsAdditional search keywordsstring[]-
disabledDisable the itembooleanfalse
onSelectCallback when item is selected() => void-
iconIcon element to displayReactNode-
data-testidTest ID for testingstring-

For more control, use compound components:

ComponentDescription
Command.ListContainer for groups and items
Command.GroupGroups items with optional heading
Command.ItemSelectable command item
Command.EmptyShown when no results match
Command.PageNested page for navigation
Command.SeparatorVisual separator between sections
  • Uses native <dialog> element for focus trapping and ESC handling
  • Search input has role="combobox" with proper ARIA attributes
  • List uses role="listbox" with role="option" items
  • Keyboard navigation with Arrow keys, Enter to select, Escape to close
  • aria-activedescendant tracks the highlighted item
  • Groups use role="group" with aria-label
  • ⌘K / Ctrl+K - Open command palette (customizable)
  • - Navigate items
  • Enter - Select highlighted item
  • Escape - Close palette (or go back on nested page)
  • Backspace - Go back to previous page (when search is empty)