Skip to content
Esc
navigateopen⌘Jpreview
On this page

Command

Fast, composable command palette.

Calendar
Search emoji
Calculator
Profile⌘P
Billing⌘B
Settings⌘S
import * as stylex from '@stylexjs/stylex';

import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandShortcut,
} from '@/components/ui/command';
import { ring } from '@/lib/stylex-utils';
import { container } from '@/lib/constants.stylex';
import { shadow } from '@/lib/tokens.stylex';

const commands = [
  { value: 'calendar', label: 'Calendar', group: 'Suggestions' },
  { value: 'search-emoji', label: 'Search emoji', group: 'Suggestions' },
  { value: 'calculator', label: 'Calculator', group: 'Suggestions' },
  { value: 'profile', label: 'Profile', group: 'Settings', shortcut: '⌘P' },
  { value: 'billing', label: 'Billing', group: 'Settings', shortcut: '⌘B' },
  { value: 'settings', label: 'Settings', group: 'Settings', shortcut: '⌘S' },
];

type CommandEntry = (typeof commands)[number];

export default function CommandDemo() {
  return (
    <Command
      items={commands}
      itemToStringLabel={(item: CommandEntry) => item.label}
      style={[styles.root, ring({ shadow: shadow.md })]}
    >
      <CommandInput placeholder="Type a command or search…" />
      <CommandEmpty>No results found.</CommandEmpty>
      <CommandList>
        {(item: CommandEntry) => (
          <CommandItem key={item.value} value={item}>
            {item.label}
            {item.shortcut && <CommandShortcut>{item.shortcut}</CommandShortcut>}
          </CommandItem>
        )}
      </CommandList>
    </Command>
  );
}

const styles = stylex.create({
  root: {
    maxWidth: container.lg,
  },
});

Install

npx shadcn@latest add @madeui/command

Usage

import {
  Command,
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandShortcut,
} from '@/components/ui/command';

Composition

<Command items={items}>
  <CommandInput />
  <CommandEmpty />
  <CommandList>
    {(item) => <CommandItem value={item} />}
  </CommandList>
</Command>

Groups

Suggestions
Calendar
Search emoji
Settings
Profile⌘P
Billing⌘B
import * as React from 'react';

import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from '@/components/ui/command';

function CalendarIcon() {
  return (
    <svg
      width="16"
      height="16"
      viewBox={`0 0 16 16`}
      fill="none"
      stroke="currentColor"
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden
    >
      <rect x="2" y="3" width="12" height="11" rx="1.5" />
      <path d={`M2 6.5h12M5 1.5v3M11 1.5v3`} />
    </svg>
  );
}

function SmileIcon() {
  return (
    <svg
      width="16"
      height="16"
      viewBox={`0 0 16 16`}
      fill="none"
      stroke="currentColor"
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden
    >
      <circle cx="8" cy="8" r="6.5" />
      <path d={`M5.5 9.5c.6.8 1.4 1.2 2.5 1.2s1.9-.4 2.5-1.2`} />
      <path d={`M6 6h.01M10 6h.01`} />
    </svg>
  );
}

function UserIcon() {
  return (
    <svg
      width="16"
      height="16"
      viewBox={`0 0 16 16`}
      fill="none"
      stroke="currentColor"
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden
    >
      <circle cx="8" cy="5.5" r="2.5" />
      <path d={`M2.5 14c.8-2.8 3-4.5 5.5-4.5s4.7 1.7 5.5 4.5`} />
    </svg>
  );
}

function CardIcon() {
  return (
    <svg
      width="16"
      height="16"
      viewBox={`0 0 16 16`}
      fill="none"
      stroke="currentColor"
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden
    >
      <rect x="1.5" y="4" width="13" height="9" rx="1.5" />
      <path d={`M1.5 7h13`} />
    </svg>
  );
}

const commandGroups = [
  {
    value: 'suggestions',
    label: 'Suggestions',
    items: [
      { value: 'calendar', label: 'Calendar', icon: <CalendarIcon /> },
      { value: 'search-emoji', label: 'Search emoji', icon: <SmileIcon /> },
    ],
  },
  {
    value: 'settings',
    label: 'Settings',
    items: [
      {
        value: 'profile',
        label: 'Profile',
        icon: <UserIcon />,
        shortcut: '⌘P',
      },
      { value: 'billing', label: 'Billing', icon: <CardIcon />, shortcut: '⌘B' },
    ],
  },
];

export default function CommandGroups() {
  return (
    <Command items={commandGroups}>
      <CommandInput placeholder="Type a command or search…" />
      <CommandEmpty>No results found.</CommandEmpty>
      <CommandList>
        {(group: (typeof commandGroups)[number]) => (
          <React.Fragment key={group.value}>
            <CommandGroup heading={group.label}>
              {group.items.map((item) => (
                <CommandItem key={item.value} value={item}>
                  {item.icon}
                  {item.label}
                  {'shortcut' in item && item.shortcut && (
                    <CommandShortcut>{item.shortcut}</CommandShortcut>
                  )}
                </CommandItem>
              ))}
            </CommandGroup>
            {group.value !==
              commandGroups[commandGroups.length - 1].value && (
              <CommandSeparator />
            )}
          </React.Fragment>
        )}
      </CommandList>
    </Command>
  );
}

Scrollable

Apple
Apricot
Banana
Blackberry
Blueberry
Cantaloupe
Cherry
Clementine
Coconut
Cranberry
Date
Dragonfruit
Elderberry
Fig
Grape
Grapefruit
Guava
Honeydew
Kiwi
Lemon
Lime
Lychee
Mango
Mandarin
Nectarine
Orange
Papaya
Passionfruit
Peach
Pear
Persimmon
Pineapple
Plum
Pomegranate
Raspberry
Star fruit
Strawberry
Tangerine
Watermelon
import {
  Command,
  CommandEmpty,
  CommandInput,
  CommandItem,
  CommandList,
} from '@/components/ui/command';

const fruits = [
  'Apple', 'Apricot', 'Banana', 'Blackberry', 'Blueberry', 'Cantaloupe',
  'Cherry', 'Clementine', 'Coconut', 'Cranberry', 'Date', 'Dragonfruit',
  'Elderberry', 'Fig', 'Grape', 'Grapefruit', 'Guava', 'Honeydew', 'Kiwi',
  'Lemon', 'Lime', 'Lychee', 'Mango', 'Mandarin', 'Nectarine', 'Orange',
  'Papaya', 'Passionfruit', 'Peach', 'Pear', 'Persimmon', 'Pineapple',
  'Plum', 'Pomegranate', 'Raspberry', 'Star fruit', 'Strawberry',
  'Tangerine', 'Watermelon',
].map((label) => ({ label, value: label }));

export default function CommandScrollable() {
  return (
    <Command items={fruits} itemToStringLabel={(item) => item.label}>
      <CommandInput placeholder="Search fruit…" />
      <CommandEmpty>No results found.</CommandEmpty>
      <CommandList>
        {(item: (typeof fruits)[number]) => (
          <CommandItem key={item.value} value={item}>
            {item.label}
          </CommandItem>
        )}
      </CommandList>
    </Command>
  );
}

Keyboard shortcut

Open the palette with ⌘K / Ctrl K using the useHotkey helper (npx shadcn@latest add @madeui/use-hotkey).

'use client';

import * as React from 'react';

import { Button } from '@/components/ui/button';
import {
  Command,
  CommandDialog,
  CommandEmpty,
  CommandInput,
  CommandItem,
  CommandList,
} from '@/components/ui/command';
import { Kbd, KbdGroup } from '@/components/ui/kbd';
import { useHotkey } from '@/lib/use-hotkey';

const commands = [
  { value: 'calendar', label: 'Calendar' },
  { value: 'search-emoji', label: 'Search emoji' },
  { value: 'calculator', label: 'Calculator' },
];

type CommandEntry = (typeof commands)[number];

export default function CommandDialogHotkey() {
  const [open, setOpen] = React.useState(false);
  useHotkey('k', () => setOpen((value) => !value));

  return (
    <>
      <Button variant="outline" onClick={() => setOpen(true)}>
        Open command palette
        <KbdGroup>
          <Kbd>⌘</Kbd>
          <Kbd>K</Kbd>
        </KbdGroup>
      </Button>
      <CommandDialog open={open} onOpenChange={setOpen}>
        <Command
          items={commands}
          itemToStringLabel={(item: CommandEntry) => item.label}
        >
          <CommandInput placeholder="Type a command or search…" />
          <CommandEmpty>No results found.</CommandEmpty>
          <CommandList>
            {(item: CommandEntry) => (
              <CommandItem
                key={item.value}
                value={item}
                onClick={() => setOpen(false)}
              >
                {item.label}
              </CommandItem>
            )}
          </CommandList>
        </Command>
      </CommandDialog>
    </>
  );
}

API reference

Built on Base UI Autocomplete in inline open mode — the filtered list renders in place with no popup, like a command palette, with no extra dependency. Pass items to Command; filtering and highlight ship with the primitive, and CommandList accepts a render function over the filtered items. All other props are forwarded to the Base UI Root (filter, onValueChange, itemToStringLabel, …); see the Base UI Autocomplete API reference.

CommandDialog

Wraps the palette in our Dialog (installed as a dependency), positioned at the top third with no close button.

Prop Type Default Description
title string 'Command Palette' Screen-reader-only dialog title.
description string 'Search for a command to run…' Screen-reader-only description.

CommandGroup

Prop Type Default Description
heading ReactNode Renders a muted group label above the items.

Every part accepts a style prop (StyleXStyles, merged last).

Was this page helpful?