Command
A keyboard-driven palette for searching and running actions.
Command composes Base UI's Autocomplete and Dialog primitives, so filtering,
keyboard navigation and grouping share one model. Every part carries a
data-slot for styling.
Installation
npx shadcn@latest add @kosori/commandExample
The Example below is a registry:example item, built and validated by the same
pipeline as Command. Its source is the file @kosori/command-example installs,
so the preview and the install cannot drift.
command-example.tsx
'use client';
import {
IconArrowDown,
IconArrowUp,
IconCornerDownLeft,
} from '@tabler/icons-react';
import { Fragment, useEffect, useState } from 'react';
import { Button } from '@/components/ui/button';
import {
Command,
CommandCollection,
CommandDialog,
CommandDialogPopup,
CommandDialogTrigger,
CommandEmpty,
CommandFooter,
CommandGroup,
CommandGroupLabel,
CommandInput,
CommandItem,
CommandList,
CommandPanel,
CommandSeparator,
CommandShortcut,
} from '@/components/ui/command';
import { Kbd, KbdGroup } from '@/components/ui/kbd';
type Item = {
label: string;
shortcut?: string;
value: string;
};
type Group = {
items: Item[];
value: string;
};
const suggestions: Item[] = [
{ label: 'Linear', shortcut: '⌘L', value: 'linear' },
{ label: 'Figma', shortcut: '⌘F', value: 'figma' },
{ label: 'Slack', shortcut: '⌘S', value: 'slack' },
{ label: 'YouTube', shortcut: '⌘Y', value: 'youtube' },
{ label: 'Raycast', shortcut: '⌘R', value: 'raycast' },
];
const commands: Item[] = [
{ label: 'Clipboard History', shortcut: '⌘⇧C', value: 'clipboard-history' },
{ label: 'Import Extension', shortcut: '⌘I', value: 'import-extension' },
{ label: 'Create Snippet', shortcut: '⌘N', value: 'create-snippet' },
{ label: 'System Preferences', shortcut: '⌘,', value: 'system-preferences' },
{
label: 'Window Management',
shortcut: '⌘⇧W',
value: 'window-management',
},
];
const groupedItems: Group[] = [
{ items: suggestions, value: 'Suggestions' },
{ items: commands, value: 'Commands' },
];
const CommandExample = () => {
const [open, setOpen] = useState(false);
useEffect(() => {
const down = (event: KeyboardEvent) => {
if (event.key === 'j' && (event.metaKey || event.ctrlKey)) {
event.preventDefault();
setOpen((open) => !open);
}
};
document.addEventListener('keydown', down);
return () => document.removeEventListener('keydown', down);
}, []);
return (
<CommandDialog onOpenChange={setOpen} open={open}>
<CommandDialogTrigger render={<Button variant='outline' />}>
Open Command Palette
<KbdGroup>
<Kbd>⌘</Kbd>
<Kbd>J</Kbd>
</KbdGroup>
</CommandDialogTrigger>
<CommandDialogPopup>
<Command items={groupedItems}>
<CommandInput placeholder='Search for apps and commands...' />
<CommandPanel>
<CommandEmpty>No results found.</CommandEmpty>
<CommandList>
{(group: Group) => (
<Fragment key={group.value}>
<CommandGroup items={group.items}>
<CommandGroupLabel>{group.value}</CommandGroupLabel>
<CommandCollection>
{(item: Item) => (
<CommandItem
key={item.value}
// biome-ignore lint/performance/noJsxPropsBind: the handler closes over the dialog state
onClick={() => setOpen(false)}
value={item.value}
>
<span className='flex-1'>{item.label}</span>
{/* biome-ignore lint/suspicious/noLeakedRender: shortcut is a checked string */}
{item.shortcut && (
<CommandShortcut>{item.shortcut}</CommandShortcut>
)}
</CommandItem>
)}
</CommandCollection>
</CommandGroup>
<CommandSeparator />
</Fragment>
)}
</CommandList>
</CommandPanel>
<CommandFooter>
<div className='flex items-center gap-4'>
<div className='flex items-center gap-2'>
<KbdGroup>
<Kbd>
<IconArrowUp />
</Kbd>
<Kbd>
<IconArrowDown />
</Kbd>
</KbdGroup>
<span>Navigate</span>
</div>
<div className='flex items-center gap-2'>
<Kbd>
<IconCornerDownLeft />
</Kbd>
<span>Open</span>
</div>
</div>
<div className='flex items-center gap-2'>
<Kbd>Esc</Kbd>
<span>Close</span>
</div>
</CommandFooter>
</Command>
</CommandDialogPopup>
</CommandDialog>
);
};
export default CommandExample;
Usage
import {
Command,
CommandEmpty,
CommandGroup,
CommandGroupLabel,
CommandInput,
CommandItem,
CommandList,
} from '@/components/ui/command';
const CommandDemo = () => (
<Command>
<CommandInput placeholder='Search commands' />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup>
<CommandGroupLabel>Suggestions</CommandGroupLabel>
<CommandItem>Calendar</CommandItem>
</CommandGroup>
</CommandList>
</Command>
);