Docs
Command
Command
Composable command menu.
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandShortcut,
} from "@repo/tailwindcss/ui/command";
import { For, type JSXElement } from "solid-js";
type Option = {
icon: JSXElement;
label: string;
disabled: boolean;
shortcut?: JSXElement;
};
type List = {
label: string;
options: Option[];
};
export const commandData: List[] = [
{
label: "Suggestions",
options: [
{
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="mr-2 h-4 w-4"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm12-4v4M8 3v4m-4 4h16m-9 4h1m0 0v3"
/>
</svg>
),
label: "Calendar",
disabled: false,
},
{
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-4 w-4"
viewBox="0 0 24 24"
>
<g
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
>
<path d="M3 12a9 9 0 1 0 18 0a9 9 0 1 0-18 0m6-2h.01M15 10h.01" />
<path d="M9.5 15a3.5 3.5 0 0 0 5 0" />
</g>
</svg>
),
label: "Search emoji",
disabled: false,
},
{
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-4 w-4"
viewBox="0 0 24 24"
>
<g
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
>
<path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3-5a9 9 0 0 0 6-8a3 3 0 0 0-3-3a9 9 0 0 0-8 6a6 6 0 0 0-5 3" />
<path d="M7 14a6 6 0 0 0-3 6a6 6 0 0 0 6-3m4-8a1 1 0 1 0 2 0a1 1 0 1 0-2 0" />
</g>
</svg>
),
label: "Launch",
disabled: false,
},
],
},
{
label: "Settings",
options: [
{
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-4 w-4"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0-8 0M6 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2"
/>
</svg>
),
label: "Profile",
disabled: true,
shortcut: <CommandShortcut>⌘P</CommandShortcut>,
},
{
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-4 w-4"
viewBox="0 0 24 24"
>
<g
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
>
<path d="M3 7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<path d="m3 7l9 6l9-6" />
</g>
</svg>
),
label: "Mail",
disabled: false,
shortcut: <CommandShortcut>⌘B</CommandShortcut>,
},
{
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-2 h-4 w-4"
viewBox="0 0 24 24"
>
<g
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
>
<path d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 0 0-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 0 0-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 0 0-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 0 0-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 0 0 1.066-2.573c-.94-1.543.826-3.31 2.37-2.37c1 .608 2.296.07 2.572-1.065" />
<path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0-6 0" />
</g>
</svg>
),
label: "Setting",
disabled: false,
shortcut: <CommandShortcut>⌘S</CommandShortcut>,
},
],
},
];
const CommandDemo = () => {
return (
<Command class="rounded-lg border shadow-md">
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<For each={commandData}>
{(item) => (
<CommandGroup heading={item.label}>
<For each={item.options}>
{(item) => (
<CommandItem disabled={item.disabled}>
{item.icon}
<span>{item.label}</span>
{item.shortcut}
</CommandItem>
)}
</For>
</CommandGroup>
)}
</For>
</CommandList>
</Command>
);
};
export default CommandDemo;
Installation
npx shadcn-solid@latest add command
Install the following dependencies:
npm install cmdk-solid
Copy and paste the following code into your project:
import { cn } from "@/libs/cn";
import type {
CommandDialogProps,
CommandEmptyProps,
CommandGroupProps,
CommandInputProps,
CommandItemProps,
CommandListProps,
CommandRootProps,
} from "cmdk-solid";
import { Command as CommandPrimitive } from "cmdk-solid";
import type { ComponentProps, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
import { Dialog, DialogContent } from "./dialog";
export const Command = (props: CommandRootProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive
class={cn(
"flex size-full flex-col overflow-hidden bg-popover text-popover-foreground",
local.class,
)}
{...rest}
/>
);
};
export const CommandList = (props: CommandListProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.List
class={cn(
"max-h-[300px] overflow-y-auto overflow-x-hidden p-1",
local.class,
)}
{...rest}
/>
);
};
export const CommandInput = (props: VoidProps<CommandInputProps>) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div class="flex items-center border-b px-3" cmdk-input-wrapper="">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="mr-2 h-4 w-4 shrink-0 opacity-50"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 10a7 7 0 1 0 14 0a7 7 0 1 0-14 0m18 11l-6-6"
/>
<title>Search</title>
</svg>
<CommandPrimitive.Input
class={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
local.class,
)}
{...rest}
/>
</div>
);
};
export const CommandItem = (props: CommandItemProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-selected:bg-accent aria-selected:text-accent-foreground",
local.class,
)}
{...rest}
/>
);
};
export const CommandShortcut = (props: ComponentProps<"span">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<span
class={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
local.class,
)}
{...rest}
/>
);
};
export const CommandDialog = (props: CommandDialogProps) => {
const [local, rest] = splitProps(props, ["children"]);
return (
<Dialog {...rest}>
<DialogContent class="overflow-hidden p-0">
<Command class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:size-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:size-5">
{local.children}
</Command>
</DialogContent>
</Dialog>
);
};
export const CommandEmpty = (props: CommandEmptyProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Empty
class={cn("py-6 text-center text-sm", local.class)}
{...rest}
/>
);
};
export const CommandGroup = (props: CommandGroupProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Group
class={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",
local.class,
)}
{...rest}
/>
);
};
export const CommandSeparator = (props: CommandEmptyProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Separator
class={cn("-mx-1 h-px bg-border", local.class)}
{...rest}
/>
);
};
Install the following dependencies:
npm install cmdk-solid
Copy and paste the following code into your project:
import { cn } from "@/libs/cn";
import type {
CommandDialogProps,
CommandEmptyProps,
CommandGroupProps,
CommandInputProps,
CommandItemProps,
CommandListProps,
CommandRootProps,
} from "cmdk-solid";
import { Command as CommandPrimitive } from "cmdk-solid";
import type { ComponentProps, VoidProps } from "solid-js";
import { splitProps } from "solid-js";
import { Dialog, DialogContent } from "./dialog";
export const Command = (props: CommandRootProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive
class={cn(
"flex size-full flex-col overflow-hidden bg-popover text-popover-foreground",
local.class,
)}
{...rest}
/>
);
};
export const CommandList = (props: CommandListProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.List
class={cn(
"max-h-[300px] overflow-y-auto overflow-x-hidden p-1",
local.class,
)}
{...rest}
/>
);
};
export const CommandInput = (props: VoidProps<CommandInputProps>) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<div class="flex items-center border-b px-3" cmdk-input-wrapper="">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="mr-2 h-4 w-4 shrink-0 opacity-50"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 10a7 7 0 1 0 14 0a7 7 0 1 0-14 0m18 11l-6-6"
/>
<title>Search</title>
</svg>
<CommandPrimitive.Input
class={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:(cursor-not-allowed opacity-50)",
local.class,
)}
{...rest}
/>
</div>
);
};
export const CommandItem = (props: CommandItemProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:(bg-accent text-accent-foreground) aria-disabled:(pointer-events-none opacity-50)",
local.class,
)}
{...rest}
/>
);
};
export const CommandShortcut = (props: ComponentProps<"span">) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<span
class={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
local.class,
)}
{...rest}
/>
);
};
export const CommandDialog = (props: CommandDialogProps) => {
const [local, rest] = splitProps(props, ["children"]);
return (
<Dialog {...rest}>
<DialogContent class="overflow-hidden p-0">
<Command class="[&_[cmdk-group-heading]]:(px-2 font-medium text-muted-foreground) [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:size-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:(px-2 py-3) [&_[cmdk-item]_svg]:size-5">
{local.children}
</Command>
</DialogContent>
</Dialog>
);
};
export const CommandEmpty = (props: CommandEmptyProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Empty
class={cn("py-6 text-center text-sm", local.class)}
{...rest}
/>
);
};
export const CommandGroup = (props: CommandGroupProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Group
class={cn(
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:(px-2 py-1.5 text-xs font-medium text-muted-foreground)",
local.class,
)}
{...rest}
/>
);
};
export const CommandSeparator = (props: CommandEmptyProps) => {
const [local, rest] = splitProps(props, ["class"]);
return (
<CommandPrimitive.Separator
class={cn("-mx-1 h-px bg-border", local.class)}
{...rest}
/>
);
};
Usage
import {
Command,
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from "@/components/ui/command"
<Command>
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions">
<CommandItem>Calendar</CommandItem>
<CommandItem>Search Emoji</CommandItem>
<CommandItem>Calculator</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Settings">
<CommandItem>Profile</CommandItem>
<CommandItem>Billing</CommandItem>
<CommandItem>Settings</CommandItem>
</CommandGroup>
</CommandList>
</Command>
Examples
Dialog
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@repo/tailwindcss/ui/command";
import { For, createEffect, createSignal, onCleanup } from "solid-js";
import { commandData } from "./command-demo";
const CommandDialogDemo = () => {
const [open, setOpen] = createSignal(false);
createEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === "j" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
setOpen((open) => !open);
}
};
document.addEventListener("keydown", down);
onCleanup(() => {
document.removeEventListener("keydown", down);
});
});
return (
<>
<p class="text-sm text-muted-foreground">
Press{" "}
<kbd class="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">
<span class="text-xs">⌘</span>J
</kbd>
</p>
<CommandDialog
class="rounded-lg border shadow-md"
open={open()}
onOpenChange={setOpen}
>
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<For each={commandData}>
{(item) => (
<CommandGroup heading={item.label}>
<For each={item.options}>
{(item) => (
<CommandItem disabled={item.disabled}>
{item.icon}
<span>{item.label}</span>
{item.shortcut}
</CommandItem>
)}
</For>
</CommandGroup>
)}
</For>
</CommandList>
</CommandDialog>
</>
);
};
export default CommandDialogDemo;
To show the command menu in a dialog, use the <CommandDialog />
component.
export function CommandMenu() {
const [open, setOpen] = createSignal(false);
createEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === "j" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
setOpen(open => !open);
}
};
document.addEventListener("keydown", down);
onCleanup(() => {
document.removeEventListener("keydown", down);
});
});
return (
<CommandDialog open={open()} onOpenChange={setOpen}>
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions">
<CommandItem>Calendar</CommandItem>
<CommandItem>Search Emoji</CommandItem>
<CommandItem>Calculator</CommandItem>
</CommandGroup>
</CommandList>
</CommandDialog>
);
}