suiss/ui
  • Docs
  • Components
  • Patterns
  • Interactions
  • Charts
  • Blocks
GitHubGet started
DocsComponentsPatternsInteractionsChartsBlocks
Patterns19
  • Overview
  • Action Bar
  • Action Menu
  • App Shell
  • Command Palette
  • Confirm
  • Context Actions
  • Date Picker
  • Flyout
  • Modal
  • Navbar
  • Paged List
  • Panel
  • Picker
  • Preview
  • Search Picker
  • Segment Picker
  • Split View
  • Table List
  • Trail

suiss/ui · Pattern

Command Palette

A searchable command menu that opens in a dialog on desktop and a drawer on mobile.

Command Palette is an open-source pattern from suiss/ui for React. The @esuiss/ui CLI copies its source into your project, so you own the code.

import {
  CalculatorIcon,
  CalendarBlankIcon,
  CreditCardIcon,
  GearIcon,
  SmileyIcon,
  UserIcon,
} from "@phosphor-icons/react"
import {
  CommandPalette,
  CommandPaletteContent,
  CommandPaletteTrigger,
} from "@/components/patterns/command-palette"
import { Button } from "@/components/ui/button"
import {
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from "@/components/ui/command"
import { Kbd, KbdGroup } from "@/components/ui/kbd"

function PaletteItems() {
  return (
    <>
      <CommandInput placeholder="Type a command or search..." />
      <CommandList>
        <CommandEmpty>No results found.</CommandEmpty>
        <CommandGroup heading="Suggestions">
          <CommandItem>
            <CalendarBlankIcon />
            Calendar
          </CommandItem>
          <CommandItem>
            <SmileyIcon />
            Search emoji
          </CommandItem>
          <CommandItem disabled>
            <CalculatorIcon />
            Calculator
          </CommandItem>
        </CommandGroup>
        <CommandSeparator />
        <CommandGroup heading="Settings">
          <CommandItem>
            <UserIcon />
            Profile
            <CommandShortcut mod>P</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <CreditCardIcon />
            Billing
            <CommandShortcut mod>B</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <GearIcon />
            Settings
            <CommandShortcut mod>S</CommandShortcut>
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </>
  )
}

function Trigger() {
  return (
    <CommandPaletteTrigger render={<Button variant="outline" />}>
      Search
      <KbdGroup>
        <Kbd mod />
        <Kbd>K</Kbd>
      </KbdGroup>
    </CommandPaletteTrigger>
  )
}

export default function CommandPaletteDemo() {
  return (
    <div className="flex min-h-svh items-center justify-center">
      <CommandPalette>
        <Trigger />
        <CommandPaletteContent>
          <PaletteItems />
        </CommandPaletteContent>
      </CommandPalette>
    </div>
  )
}

CommandPalette hosts a Command list inside a Dialog on desktop, positioned in the upper third of the viewport, and inside a bottom Drawer with a swipe handle below the 48rem breakpoint. Fill CommandPaletteContent with CommandInput, CommandList, CommandGroup, CommandItem and the other Command parts.

A global keyboard shortcut toggles the palette: Cmd+K on macOS and Ctrl+K elsewhere by default. Use it for app-wide search and quick actions.

Installation

CLI

$ pnpm dlx shadcn@latest add @esuiss/command-palette

Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add patterns command-palette

Manual

  1. Add the cn helper.

  2. Add the pieces it builds on: Command, Dialog, Drawer, use-mobile, use-platform.

  3. Copy the source into components/patterns/command-palette.tsx:

components/patterns/command-palette.tsx
"use client"

import * as React from "react"
import { Command } from "@/components/ui/command"
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import {
  Drawer,
  DrawerContent,
  DrawerDescription,
  1. Update the import paths to match your project.

Usage

import { CommandPalette, CommandPaletteTrigger, CommandPaletteContent } from "@/components/patterns/command-palette"
<div className="flex min-h-svh items-center justify-center">
  <CommandPalette>
    <Trigger />
    <CommandPaletteContent>
      <PaletteItems />
    </CommandPaletteContent>
  </CommandPalette>
</div>

The palette can be controlled with open and onOpenChange or uncontrolled with defaultOpen; the hotkey and the trigger update the same state. Set hotkey to another letter to change the shortcut, or to false to disable it. CommandPaletteTrigger accepts render to use your own button, for example a Button with a Kbd hint. Remaining props on CommandPaletteContent pass through to Command.

Examples

With close button

Shows an icon close button with showCloseButton.

import {
  CalculatorIcon,
  CalendarBlankIcon,
  CreditCardIcon,
  GearIcon,
  SmileyIcon,
  UserIcon,
} from "@phosphor-icons/react"
import {
  CommandPalette,
  CommandPaletteContent,
  CommandPaletteTrigger,
} from "@/components/patterns/command-palette"
import { Button } from "@/components/ui/button"
import {
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from "@/components/ui/command"
import { Kbd, KbdGroup } from "@/components/ui/kbd"

function PaletteItems() {
  return (
    <>
      <CommandInput placeholder="Type a command or search..." />
      <CommandList>
        <CommandEmpty>No results found.</CommandEmpty>
        <CommandGroup heading="Suggestions">
          <CommandItem>
            <CalendarBlankIcon />
            Calendar
          </CommandItem>
          <CommandItem>
            <SmileyIcon />
            Search emoji
          </CommandItem>
          <CommandItem disabled>
            <CalculatorIcon />
            Calculator
          </CommandItem>
        </CommandGroup>
        <CommandSeparator />
        <CommandGroup heading="Settings">
          <CommandItem>
            <UserIcon />
            Profile
            <CommandShortcut mod>P</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <CreditCardIcon />
            Billing
            <CommandShortcut mod>B</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <GearIcon />
            Settings
            <CommandShortcut mod>S</CommandShortcut>
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </>
  )
}

function Trigger() {
  return (
    <CommandPaletteTrigger render={<Button variant="outline" />}>
      Search
      <KbdGroup>
        <Kbd mod />
        <Kbd>K</Kbd>
      </KbdGroup>
    </CommandPaletteTrigger>
  )
}

export default function CommandPaletteWithCloseButton() {
  return (
    <div className="flex min-h-svh items-center justify-center">
      <CommandPalette>
        <Trigger />
        <CommandPaletteContent showCloseButton>
          <PaletteItems />
        </CommandPaletteContent>
      </CommandPalette>
    </div>
  )
}

With close label

Shows a text close button labelled Cancel with closeLabel.

import {
  CalculatorIcon,
  CalendarBlankIcon,
  CreditCardIcon,
  GearIcon,
  SmileyIcon,
  UserIcon,
} from "@phosphor-icons/react"
import {
  CommandPalette,
  CommandPaletteContent,
  CommandPaletteTrigger,
} from "@/components/patterns/command-palette"
import { Button } from "@/components/ui/button"
import {
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from "@/components/ui/command"
import { Kbd, KbdGroup } from "@/components/ui/kbd"

function PaletteItems() {
  return (
    <>
      <CommandInput placeholder="Type a command or search..." />
      <CommandList>
        <CommandEmpty>No results found.</CommandEmpty>
        <CommandGroup heading="Suggestions">
          <CommandItem>
            <CalendarBlankIcon />
            Calendar
          </CommandItem>
          <CommandItem>
            <SmileyIcon />
            Search emoji
          </CommandItem>
          <CommandItem disabled>
            <CalculatorIcon />
            Calculator
          </CommandItem>
        </CommandGroup>
        <CommandSeparator />
        <CommandGroup heading="Settings">
          <CommandItem>
            <UserIcon />
            Profile
            <CommandShortcut mod>P</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <CreditCardIcon />
            Billing
            <CommandShortcut mod>B</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <GearIcon />
            Settings
            <CommandShortcut mod>S</CommandShortcut>
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </>
  )
}

function Trigger() {
  return (
    <CommandPaletteTrigger render={<Button variant="outline" />}>
      Search
      <KbdGroup>
        <Kbd mod />
        <Kbd>K</Kbd>
      </KbdGroup>
    </CommandPaletteTrigger>
  )
}

export default function CommandPaletteWithCloseLabel() {
  return (
    <div className="flex min-h-svh items-center justify-center">
      <CommandPalette>
        <Trigger />
        <CommandPaletteContent showCloseButton closeLabel="Cancel">
          <PaletteItems />
        </CommandPaletteContent>
      </CommandPalette>
    </div>
  )
}

Floating

Insets the mobile drawer from the screen edges with floating.

import {
  CalculatorIcon,
  CalendarBlankIcon,
  CreditCardIcon,
  GearIcon,
  SmileyIcon,
  UserIcon,
} from "@phosphor-icons/react"
import {
  CommandPalette,
  CommandPaletteContent,
  CommandPaletteTrigger,
} from "@/components/patterns/command-palette"
import { Button } from "@/components/ui/button"
import {
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from "@/components/ui/command"
import { Kbd, KbdGroup } from "@/components/ui/kbd"

function PaletteItems() {
  return (
    <>
      <CommandInput placeholder="Type a command or search..." />
      <CommandList>
        <CommandEmpty>No results found.</CommandEmpty>
        <CommandGroup heading="Suggestions">
          <CommandItem>
            <CalendarBlankIcon />
            Calendar
          </CommandItem>
          <CommandItem>
            <SmileyIcon />
            Search emoji
          </CommandItem>
          <CommandItem disabled>
            <CalculatorIcon />
            Calculator
          </CommandItem>
        </CommandGroup>
        <CommandSeparator />
        <CommandGroup heading="Settings">
          <CommandItem>
            <UserIcon />
            Profile
            <CommandShortcut mod>P</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <CreditCardIcon />
            Billing
            <CommandShortcut mod>B</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <GearIcon />
            Settings
            <CommandShortcut mod>S</CommandShortcut>
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </>
  )
}

function Trigger() {
  return (
    <CommandPaletteTrigger render={<Button variant="outline" />}>
      Search
      <KbdGroup>
        <Kbd mod />
        <Kbd>K</Kbd>
      </KbdGroup>
    </CommandPaletteTrigger>
  )
}

export default function CommandPaletteFloating() {
  return (
    <div className="flex min-h-svh items-center justify-center">
      <CommandPalette floating>
        <Trigger />
        <CommandPaletteContent>
          <PaletteItems />
        </CommandPaletteContent>
      </CommandPalette>
    </div>
  )
}

API Reference

Anatomy2 parts
<CommandPalette>
<CommandPaletteContent />
</CommandPalette>
01

CommandPalette

The root that renders a Dialog on desktop and a Drawer on mobile and registers the hotkey.

PropTypeDefaultDescription
onOpenChange(open: boolean) => void-Called when the palette opens or closes, including through the hotkey.
hotkeystring | false"k"The letter that toggles the palette together with Cmd on macOS or Ctrl elsewhere; false disables it.
floatingboolean-Insets the mobile drawer from the screen edges with fully rounded corners.

Also accepts the props of React.ComponentProps<typeof Dialog>.

02

CommandPaletteTrigger

The button that opens the palette.

PropTypeDefaultDescription
renderReact.ReactElement-Replaces the default button element with your own element, such as a Button.

Also accepts the props of React.ButtonHTMLAttributes<HTMLButtonElement>.

03

CommandPaletteContent

The popup that wraps a Command with a visually hidden title and description.

PropTypeDefaultDescription
titlestring"Command Palette"Accessible name of the dialog, rendered visually hidden.
descriptionstring"Search for a command to run..."Accessible description of the dialog, rendered visually hidden.
showCloseButtonbooleanfalseShows a close button in the corner of the popup.
closeLabelstring-Replaces the close icon with this text; requires showCloseButton.

Also accepts the props of React.ComponentProps<typeof Command>.

Accessibility

  • The popup is a dialog named by title and described by description.
  • Opening the palette focuses the search input, and the first enabled option is selected.
  • Arrow keys move between options and skip disabled ones; typing filters the list.
  • Escape closes the palette and returns focus to the trigger.

Notes

Parts carry data-slot values command-palette, command-palette-trigger and command-palette-content. On mobile the list is capped at 60dvh and the input gains end padding when a close button is shown. className on CommandPaletteContent applies to the inner Command, not the dialog or drawer.

NextConfirmPreviousApp Shell

On this page

  1. 01Installation
    • CLI
    • Manual
  2. 02Usage
  3. 03Examples
    • With close button
    • With close label
    • Floating
  4. 04API Reference
    • CommandPalette
    • CommandPaletteTrigger
    • CommandPaletteContent
  5. 05Accessibility
  6. 06Notes