shadcn.io is not affiliated with official shadcn/ui
Shadcn Command for React and Tailwind
Searchable command input with filtering.
Suggestions
Linear⌘L
Figma⌘F
Slack⌘S
YouTube⌘Y
Raycast⌘R
Settings
Settings⌘,
Help⌘?
About⌘I
Feedback⌘B
Support⌘P
Updates⌘U
Logout⌘Q
Sign out⌘O
Sign in⌘A
Installation
bunx --bun shadcn@latest add https://kit.dev/r/command.jsonpnpm dlx shadcn@latest add https://kit.dev/r/command.jsonnpx shadcn@latest add https://kit.dev/r/command.jsonyarn shadcn@latest add https://kit.dev/r/command.jsonThis component depends on Combobox, Dialog, Input, Input Group, Menu and Separator. Install them first if you haven't already.
Install the following dependencies:
bun add @ark-ui/react lucide-reactpnpm add @ark-ui/react lucide-reactnpm install @ark-ui/react lucide-reactyarn add @ark-ui/react lucide-reactCopy and paste the following code into your project.
"use client";
import { Combobox as ArkCombobox } from "@ark-ui/react/combobox";
import { Dialog as ArkDialog } from "@ark-ui/react/dialog";
import { Portal } from "@ark-ui/react/portal";
import { SearchIcon } from "lucide-react";
import type React from "react";
import { cn } from "@/lib/utils";
import {
Combobox,
ComboboxContext,
ComboboxControl,
ComboboxEmpty,
ComboboxGroup,
ComboboxGroupLabel,
ComboboxItemContext,
ComboboxList,
ComboboxRootProvider,
comboboxItemVariants,
useCombobox,
useComboboxContext,
useComboboxItemContext,
} from "@/components/ui/combobox";
import {
Dialog,
type DialogContent,
DialogHeader,
DialogOverlay,
DialogPositioner,
DialogTrigger,
dialogContentVariants,
} from "@/components/ui/dialog";
import type { InputProps } from "@/components/ui/input";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
import { MenuShortcut } from "@/components/ui/menu";
import { Separator } from "@/components/ui/separator";
export const useCommand: typeof useCombobox = (props) =>
useCombobox({
closeOnSelect: false,
disableLayer: true,
inputBehavior: "autohighlight",
loopFocus: false,
open: true,
selectionBehavior: "clear",
...props,
});
export const useCommandContext = useComboboxContext;
export const useCommandItemContext = useComboboxItemContext;
export const CommandContext: typeof ComboboxContext = ComboboxContext;
export const CommandItemContext: typeof ComboboxItemContext =
ComboboxItemContext;
const commandRootClassName = cn(
"isolate",
"flex min-h-0 flex-1 flex-col",
"p-2",
"bg-popover",
"text-popover-foreground",
"rounded-2xl border"
);
export const CommandDialog = Dialog;
export const CommandDialogTrigger = (
props: React.ComponentProps<typeof DialogTrigger>
) => <DialogTrigger data-slot="command-dialog-trigger" {...props} />;
interface CommandDialogContentProps
extends React.ComponentProps<typeof DialogContent> {
/**
* The description of the dialog
*
* @default "Search for a command to run..."
*/
description?: string;
/**
* The title of the dialog
*
* @default "Command Palette"
*/
title?: string;
}
export const CommandDialogContent = (props: CommandDialogContentProps) => {
const {
size = "lg",
title = "Command Palette",
description = "Search for a command to run...",
className,
children,
showCloseButton: _showCloseButton,
bottomStickOnMobile,
positionerClassName,
...rest
} = props;
return (
<Portal>
<DialogOverlay />
<DialogPositioner
className={cn(
bottomStickOnMobile &&
"max-sm:grid-rows-[1fr_auto] max-sm:p-0 max-sm:pt-12",
positionerClassName
)}
>
<ArkDialog.Content
className={cn(
"max-sm:row-start-1",
dialogContentVariants({ bottomStickOnMobile, size }),
"border-0 p-0",
className
)}
data-slot="command-dialog-content"
{...rest}
>
<DialogHeader
className="sr-only"
description={description}
title={title}
/>
{children}
</ArkDialog.Content>
</DialogPositioner>
</Portal>
);
};
export const Command: ArkCombobox.RootComponent = (props) => {
const { lazyMount = true, unmountOnExit = true, className, ...rest } = props;
return (
<Combobox
className={cn(commandRootClassName, className)}
closeOnSelect={false}
data-slot="command"
disableLayer
inputBehavior="autohighlight"
lazyMount={lazyMount}
loopFocus={false}
open
selectionBehavior="clear"
unmountOnExit={unmountOnExit}
{...rest}
/>
);
};
export const CommandRootProvider: ArkCombobox.RootProviderComponent = (
props
) => {
const { lazyMount = true, unmountOnExit = true, className, ...rest } = props;
return (
<ComboboxRootProvider
className={cn(commandRootClassName, className)}
data-slot="command"
lazyMount={lazyMount}
unmountOnExit={unmountOnExit}
{...rest}
/>
);
};
interface CommandInputProps
extends Omit<React.ComponentProps<typeof ArkCombobox.Input>, "size"> {
/**
* The size of the input
*
* @default "md"
*/
size?: InputProps["size"];
}
export const CommandContent = (
props: React.ComponentProps<typeof ArkCombobox.Content>
) => {
const { className, ...rest } = props;
return (
<ArkCombobox.Content
className={cn(
"flex flex-1 flex-col",
"max-h-(--available-height) min-h-0",
"-me-2",
"outline-none",
"scrollbar-thin scrollbar-track-transparent scrollbar-thumb-foreground/20 overflow-auto overscroll-contain",
"[:not(.has-[+[data-slot=command-footer]])]:rounded-b-2xl [:not(.has-[+[data-slot=command-footer]])]:border-b",
className
)}
data-slot="command-content"
{...rest}
/>
);
};
export const CommandInput = (props: CommandInputProps) => {
const { size = "md", className, placeholder, ...rest } = props;
return (
<ComboboxControl className="mb-2">
<InputGroup
className={cn("rounded-xl bg-input/32", className)}
size={size}
>
<InputGroupAddon>
<SearchIcon aria-hidden="true" className="opacity-64" />
</InputGroupAddon>
<ArkCombobox.Input asChild>
<InputGroupInput
autoFocus
data-slot="command-input"
placeholder={placeholder}
{...rest}
aria-label={rest["aria-label"] ?? placeholder ?? "Search"}
/>
</ArkCombobox.Input>
</InputGroup>
</ComboboxControl>
);
};
interface CommandListProps extends React.ComponentProps<typeof ComboboxList> {}
export const CommandList = (props: CommandListProps) => {
const { className, ...rest } = props;
return (
<div className="max-h-72 min-h-0 flex-1">
<ComboboxList
className={cn("flex-1 pe-2.5", className)}
data-slot="command-list"
{...rest}
/>
</div>
);
};
export const CommandEmpty = (
props: React.ComponentProps<typeof ComboboxEmpty>
) => {
const { className, children, ...rest } = props;
return (
<ComboboxEmpty
className={cn("py-6 text-center text-sm", className)}
data-slot="command-empty"
{...rest}
>
{children || "No results found."}
</ComboboxEmpty>
);
};
export const CommandGroup = (
props: React.ComponentProps<typeof ComboboxGroup>
) => <ComboboxGroup data-slot="command-group" {...props} />;
export const CommandGroupLabel = (
props: React.ComponentProps<typeof ComboboxGroupLabel>
) => <ComboboxGroupLabel data-slot="command-group-label" {...props} />;
export const CommandItem = (
props: React.ComponentProps<typeof ArkCombobox.Item>
) => {
const { className, ...rest } = props;
return (
<ArkCombobox.Item
className={cn(comboboxItemVariants({ showIndicator: false }), className)}
data-slot="command-item"
persistFocus
{...rest}
/>
);
};
export const CommandSeparator = (props: React.ComponentProps<"div">) => {
const { className, ...rest } = props;
return (
<Separator
className={cn("my-2", className)}
data-slot="command-separator"
{...rest}
/>
);
};
export const CommandShortcut = (
props: React.ComponentProps<typeof MenuShortcut>
) => <MenuShortcut data-slot="command-shortcut" {...props} />;
export const CommandFooter = (props: React.ComponentProps<"div">) => {
const { className, ...rest } = props;
return (
<div
className={cn(
"z-10",
"flex items-center justify-between gap-2",
"-m-2 mt-2 px-4 py-3",
"bg-muted/48",
"text-muted-foreground text-xs",
"rounded-b-[calc(var(--radius-2xl,1rem)-1px)] border-t",
className
)}
data-slot="command-footer"
{...rest}
/>
);
};Update the import paths to match your project setup.
Anatomy
CommandRoot
├── CommandClearTrigger
├── CommandContent
├── CommandControl
├── CommandInput
├── CommandItem
│ ├── CommandItemGroup
│ │ └── CommandItemGroupLabel
│ ├── CommandItemIndicator
│ └── CommandItemText
├── CommandLabel
├── CommandList
├── CommandPositioner
└── CommandTriggerUsage
import { useListCollection } from "@ark-ui/react/collection";
import { useFilter } from "@ark-ui/react/locale";
import {
Command,
CommandContent,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from "@/components/ui/command";const CommandExample = () => {
const { contains } = useFilter({ sensitivity: "base" });
const { collection, filter } = useListCollection({
filter: contains,
groupBy: (item) => item.group,
initialItems,
});
const handleInputValueChange = ({ inputValue }: { inputValue: string }) => {
filter(inputValue);
};
const groups = collection.group();
return (
<Command
className="w-full max-w-md"
collection={collection}
onInputValueChange={handleInputValueChange}
>
<CommandInput placeholder="Search..." />
<CommandContent>
<CommandEmpty />
<CommandList>
{groups.map(([group, items], index) => (
<CommandGroup heading={group} key={group}>
{items.map((item) => (
<CommandItem item={item} key={item.value}>
{item.label}
<CommandShortcut>{item.shortcut}</CommandShortcut>
</CommandItem>
))}
{index < groups.length - 1 && <CommandSeparator />}
</CommandGroup>
))}
</CommandList>
</CommandContent>
</Command>
);
};
const initialItems = [
{ group: "Suggestions", label: "Linear", shortcut: "⌘L", value: "linear" },
{ group: "Suggestions", label: "Figma", shortcut: "⌘F", value: "figma" },
{ group: "Suggestions", label: "Slack", shortcut: "⌘S", value: "slack" },
{ group: "Suggestions", label: "YouTube", shortcut: "⌘Y", value: "youtube" },
{ group: "Suggestions", label: "Raycast", shortcut: "⌘R", value: "raycast" },
{ group: "Settings", label: "Settings", shortcut: "⌘,", value: "settings" },
{ group: "Settings", label: "Help", shortcut: "⌘?", value: "help" },
{ group: "Settings", label: "About", shortcut: "⌘I", value: "about" },
{ group: "Settings", label: "Feedback", shortcut: "⌘B", value: "feedback" },
{ group: "Settings", label: "Support", shortcut: "⌘P", value: "support" },
{ group: "Settings", label: "Updates", shortcut: "⌘U", value: "updates" },
{ group: "Settings", label: "Logout", shortcut: "⌘Q", value: "logout" },
{ group: "Settings", label: "Sign out", shortcut: "⌘O", value: "sign out" },
{ group: "Settings", label: "Sign in", shortcut: "⌘A", value: "sign in" },
];Accessibility
| Key | Description |
|---|---|
ArrowDown | Move down. |
ArrowUp | Move up. |
Home | Go to the start. |
End | Go to the end. |
Enter | Activate the focused item. |
Escape | Dismiss. |
Space | Activate the focused control. |
ArrowLeft | Move left. |
ArrowRight | Move right. |
Demos
Context
Controlled
Dialog
Groups
Root Provider
Scrollable
Shortcuts
With Footer
API Reference
CommandContent
div
PropType
AttributeType
CSS variableType
CommandInput
input
PropType
AttributeType
CommandItem
div
PropType
AttributeType
CommandDialog
AttributeType
CommandDialogTrigger
PropType
AttributeType
CommandDialogContent
div
PropType
AttributeType
CommandList
PropType
AttributeType
CommandEmpty
PropType
AttributeType
CommandGroup
PropType
AttributeType
CommandGroupLabel
PropType
AttributeType
CommandSeparator
PropType
AttributeType
CommandShortcut
PropType
AttributeType
CommandFooter
PropType
AttributeType