rapidUI

Command

Command menu / searchable list built on cmdk, usable inline or in a dialog.

Installation

pnpm dlx shadcn@latest add https://rdf.dev.360online.com/r/command.json

Installs to @/components/ui/command.

Manual installation

Install the dependencies:

pnpm add cmdk@^1.1.1 lucide-react@^1.48.0

Add the components it builds on: dialog, input-group.

Copy the source into your project:

components/ui/command.tsx
'use client'

import { Command as CommandPrimitive } from 'cmdk'
import { CheckIcon, SearchIcon } from 'lucide-react'
import type * as React from 'react'

import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { InputGroup, InputGroupAddon } from '@/components/ui/input-group'
import { cn } from '@/lib/utils'

function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {
	return (
		<CommandPrimitive
			data-slot="command"
			className={cn(
				'flex size-full flex-col overflow-hidden rounded-xl! bg-popover p-1 text-popover-foreground',
				className,
			)}
			{...props}
		/>
	)
}

function CommandDialog({
	title = 'Command Palette',
	description = 'Search for a command to run...',
	children,
	className,
	showCloseButton = false,
	...props
}: Omit<React.ComponentProps<typeof Dialog>, 'children'> & {
	title?: string
	description?: string
	className?: string
	showCloseButton?: boolean
	children: React.ReactNode
}) {
	return (
		<Dialog {...props}>
			<DialogContent
				className={cn('top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0', className)}
				showCloseButton={showCloseButton}
			>
				<DialogHeader className="sr-only">
					<DialogTitle>{title}</DialogTitle>
					<DialogDescription>{description}</DialogDescription>
				</DialogHeader>
				{children}
			</DialogContent>
		</Dialog>
	)
}

function CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {
	return (
		<div data-slot="command-input-wrapper" className="p-1 pb-0">
			<InputGroup className="h-10! rounded-lg! border-input/30 bg-input/30 shadow-none! hover:border-input/30 *:data-[slot=input-group-addon]:pl-2!">
				<CommandPrimitive.Input
					data-slot="command-input"
					className={cn(
						'w-full text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
						className,
					)}
					{...props}
				/>
				<InputGroupAddon>
					<SearchIcon className="size-4 shrink-0 opacity-50" />
				</InputGroupAddon>
			</InputGroup>
		</div>
	)
}

function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
	return (
		<CommandPrimitive.List
			data-slot="command-list"
			className={cn('no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none', className)}
			{...props}
		/>
	)
}

function CommandEmpty({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
	return (
		<CommandPrimitive.Empty data-slot="command-empty" className={cn('py-6 text-center text-sm', className)} {...props} />
	)
}

function CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {
	return (
		<CommandPrimitive.Group
			data-slot="command-group"
			className={cn(
				'overflow-hidden p-1 text-foreground **:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium **:[[cmdk-group-heading]]:text-muted-foreground',
				className,
			)}
			{...props}
		/>
	)
}

function CommandSeparator({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
	return (
		<CommandPrimitive.Separator
			data-slot="command-separator"
			className={cn('-mx-1 h-px bg-border', className)}
			{...props}
		/>
	)
}

function CommandItem({ className, children, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {
	return (
		<CommandPrimitive.Item
			data-slot="command-item"
			className={cn(
				"group/command-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none in-data-[slot=dialog-content]:rounded-lg! data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-selected:bg-accent data-selected:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
				className,
			)}
			{...props}
		>
			{children}
			<CheckIcon className="ml-auto opacity-0 group-has-data-[slot=command-shortcut]/command-item:hidden group-data-[checked=true]/command-item:opacity-100" />
		</CommandPrimitive.Item>
	)
}

function CommandShortcut({ className, ...props }: React.ComponentProps<'span'>) {
	return (
		<span
			data-slot="command-shortcut"
			className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}
			{...props}
		/>
	)
}

export {
	Command,
	CommandDialog,
	CommandInput,
	CommandList,
	CommandEmpty,
	CommandGroup,
	CommandItem,
	CommandShortcut,
	CommandSeparator,
}

A command menu component that provides a powerful and flexible command palette interface. Built on top of CMDK and Base UI Dialog, it follows WAI-ARIA design patterns and provides a fully accessible command menu experience.

Features

  • Keyboard navigation
  • Search functionality
  • Screen reader support
  • Custom styling with Tailwind CSS
  • Dialog mode support
  • Grouping and categorization
  • Keyboard shortcuts (CommandShortcut)
  • Built-in search icon in CommandInput (rendered with InputGroup)
  • Selected-state check icon on CommandItem via data-checked
  • Accessible, visually hidden title and description in CommandDialog
  • Icons inside items are sized automatically, no extra classes needed

Usage

import {
  Command,
  CommandDialog,
  CommandInput,
  CommandList,
  CommandEmpty,
  CommandGroup,
  CommandItem,
  CommandSeparator,
  CommandShortcut,
} from '@/components/ui/command'
import { CalendarIcon } from 'lucide-react'

// Basic usage
export function CommandMenu() {
  return (
    <Command>
      <CommandInput placeholder="Search cases, people or actions…" />
      <CommandList>
        <CommandEmpty>No results found.</CommandEmpty>
        <CommandGroup heading="Suggestions">
          <CommandItem>
            <CalendarIcon />
            <span>Council meeting agenda, 19 May</span>
          </CommandItem>
        </CommandGroup>
        <CommandSeparator />
        <CommandGroup heading="Settings">
          <CommandItem>
            <span>Profile</span>
            <CommandShortcut>⌘P</CommandShortcut>
          </CommandItem>
          <CommandItem>
            <span>Set out of office</span>
          </CommandItem>
        </CommandGroup>
      </CommandList>
    </Command>
  )
}

// Selected state: the check icon is shown when data-checked is true
<CommandItem value="Building permit" data-checked={selected.includes('building-permit')} onSelect={() => toggle('building-permit')}>
  Building permit
</CommandItem>

// Dialog mode
export function CommandMenuDialog() {
  const [open, setOpen] = React.useState(false)

  return (
    <>
      <Button onClick={() => setOpen(true)}>
        Search the case system
      </Button>
      <CommandDialog
        open={open}
        onOpenChange={setOpen}
        title="Search the case system"
        description="Find a case, a colleague or a page"
      >
        <Command>
          <CommandInput placeholder="Type a case number, name or page…" />
          <CommandList>
            <CommandEmpty>No results found.</CommandEmpty>
            <CommandGroup heading="Go to">
              <CommandItem>
                <span>My cases</span>
                <CommandShortcut>G C</CommandShortcut>
              </CommandItem>
            </CommandGroup>
          </CommandList>
        </Command>
      </CommandDialog>
    </>
  )
}

Examples

Basic Command Menu

A command menu for a case management system, with suggestions, recent cases (one disabled) and settings.

Selector

A command menu used as a multi-select filter. Selected items are marked with data-checked, which shows the built-in check icon.

Dialog Example

A command menu in dialog mode, opened from a search button or with ⌘J / Ctrl+J (this page uses ⌘K for the docs search). Wrap the dialog's content in Command.

Disabled

A command menu with disabled items.

Props

Command

  • value - The controlled value of the command menu
  • onValueChange - Event handler for value changes
  • filter - Custom filter function
  • loop - Whether to loop through items when navigating
  • className - Additional CSS classes

CommandDialog

  • open - The controlled open state of the dialog
  • onOpenChange - Event handler for open state changes
  • title - Screen-reader title (default 'Command Palette')
  • description - Screen-reader description (default 'Search for a command to run...')
  • showCloseButton - Whether to show the dialog close button (default false)
  • className - Additional CSS classes for the dialog content
  • children - Content to display in the dialog (required; wrap it in Command)

CommandInput

  • value - The controlled value of the input
  • onValueChange - Event handler for value changes
  • placeholder - Placeholder text
  • disabled - Whether the input is disabled

CommandList

  • children - List content (groups, items, etc.)
  • className - Additional CSS classes

CommandEmpty

  • children - Content to display when no results are found
  • className - Additional CSS classes

CommandGroup

  • heading - Group heading text
  • children - Group content (items)
  • className - Additional CSS classes

CommandItem

  • value - The value passed to onSelect and used for filtering
  • disabled - Whether the item is disabled
  • onSelect - Event handler for item selection
  • data-checked - When true, shows a check icon at the end of the item (hidden when the item has a CommandShortcut)
  • children - Item content
  • className - Additional CSS classes

CommandSeparator

  • className - Additional CSS classes

CommandShortcut

  • children - Shortcut text
  • className - Additional CSS classes

Accessibility

The Command component follows WAI-ARIA guidelines:

  • Uses proper ARIA roles (combobox, listbox, option)
  • Supports keyboard navigation:
    • ↑/↓: Navigate through items
    • Enter: Select item
    • Esc: Close menu/dialog
    • Tab/Shift+Tab: Navigate focusable elements
  • Announces results and state changes
  • Maintains focus management
  • Provides visual feedback for interaction states

Best Practices

  1. Content Organization:

    • Group related commands
    • Use clear, descriptive labels
    • Keep commands concise
    • Provide helpful shortcuts
  2. Search Experience:

    • Use clear placeholder text
    • Show helpful empty states
    • Consider fuzzy matching
    • Prioritize relevant results
  3. Visual Design:

    • Maintain consistent spacing
    • Use clear visual hierarchy
    • Provide feedback for actions
    • Consider mobile interactions
  4. Implementation:

    • Manage focus properly
    • Consider performance
    • Test edge cases
  • Dialog
  • Popover
  • Dropdown Menu
  • Select