Command
Menus & NavigationLocal guide ↗
import { Command } from "bits-ui" Inline palette
Built-in ranked filtering includes each item’s value, text and keywords
Workspace
Create new file N
Open project O
Switch branch
Tools
Open terminal
Workspace settings
No command run yet
<script lang="ts">
import { Command } from 'bits-ui';
import { FilePlus2, FolderOpen, GitBranch, Search, Settings, Terminal } from '@lucide/svelte';
let query = $state('');
let selected = $state('No command run yet');
const itemClass =
'flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2.5 text-sm text-ink-200 outline-none data-[selected]:bg-brand-600 data-[selected]:text-white aria-disabled:pointer-events-none aria-disabled:opacity-40';
</script>
<div class="mx-auto w-full max-w-xl">
<Command.Root class="panel overflow-hidden" loop>
<div class="flex items-center gap-3 border-b border-ink-800 px-4">
<Search class="size-4 shrink-0 text-ink-400" />
<Command.Input
bind:value={query}
placeholder="Search workspace commands…"
class="h-12 w-full bg-transparent text-sm text-ink-50 outline-none placeholder:text-ink-600"
/>
</div>
<Command.List class="max-h-80 overflow-y-auto p-2">
<Command.Viewport>
<Command.Empty class="px-3 py-10 text-center text-sm text-ink-400">
No command matches “{query}”.
</Command.Empty>
<Command.Group>
<Command.GroupHeading class="px-3 pb-1 pt-2 text-xs font-medium uppercase tracking-wider text-ink-600">
Workspace
</Command.GroupHeading>
<Command.GroupItems>
<Command.Item value="Create new file" keywords={['document', 'add']} class={itemClass} onSelect={() => (selected = 'Created a new file')}>
<FilePlus2 class="size-4" />
<span class="flex-1">Create new file</span>
<span class="text-xs text-ink-600">N</span>
</Command.Item>
<Command.Item value="Open project" keywords={['folder', 'workspace']} class={itemClass} onSelect={() => (selected = 'Opened a project')}>
<FolderOpen class="size-4" />
<span class="flex-1">Open project</span>
<span class="text-xs text-ink-600">O</span>
</Command.Item>
<Command.Item value="Switch branch" keywords={['git', 'checkout']} class={itemClass} onSelect={() => (selected = 'Switched branch')}>
<GitBranch class="size-4" />
<span class="flex-1">Switch branch</span>
</Command.Item>
</Command.GroupItems>
</Command.Group>
<Command.Separator class="my-2 h-px bg-ink-800" />
<Command.Group>
<Command.GroupHeading class="px-3 pb-1 pt-2 text-xs font-medium uppercase tracking-wider text-ink-600">
Tools
</Command.GroupHeading>
<Command.GroupItems>
<Command.Item value="Open terminal" keywords={['shell', 'console']} class={itemClass} onSelect={() => (selected = 'Opened the terminal')}>
<Terminal class="size-4" />
<span class="flex-1">Open terminal</span>
</Command.Item>
<Command.Item value="Workspace settings" keywords={['preferences', 'configure']} class={itemClass} onSelect={() => (selected = 'Opened workspace settings')}>
<Settings class="size-4" />
<span class="flex-1">Workspace settings</span>
</Command.Item>
</Command.GroupItems>
</Command.Group>
</Command.Viewport>
</Command.List>
</Command.Root>
<p class="mt-3 text-center text-xs text-ink-400" aria-live="polite">{selected}</p>
</div>
Dialog palette
A modal command surface toggled by its trigger or Cmd/Ctrl+K
Press Cmd/Ctrl+K while this demo is mounted.
<script lang="ts">
import { Command, Dialog } from 'bits-ui';
import { Command as CommandIcon, FileText, Search, Settings, UserRound } from '@lucide/svelte';
let open = $state(false);
let lastChoice = $state('');
$effect(() => {
function handleKeydown(event: KeyboardEvent) {
if (event.key.toLowerCase() === 'k' && (event.metaKey || event.ctrlKey)) {
event.preventDefault();
open = !open;
}
}
window.addEventListener('keydown', handleKeydown);
return () => window.removeEventListener('keydown', handleKeydown);
});
function choose(command: string) {
lastChoice = command;
open = false;
}
const itemClass =
'flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2.5 text-sm text-ink-200 outline-none data-[selected]:bg-brand-600 data-[selected]:text-white';
</script>
<div class="flex flex-col items-center gap-3">
<Dialog.Root bind:open>
<Dialog.Trigger class="btn btn-primary gap-2">
<CommandIcon class="size-4" />
Open command palette
<span class="ml-2 rounded border border-white/20 px-1.5 py-0.5 font-mono text-[0.65rem]">⌘K</span>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay class="fixed inset-0 z-40 bg-black/70 data-[state=closed]:animate-fade-out data-[state=open]:animate-fade-in" />
<Dialog.Content class="panel fixed left-1/2 top-1/2 z-50 w-[min(92vw,34rem)] -translate-x-1/2 -translate-y-1/2 overflow-hidden p-0 animate-pop-in outline-none">
<Dialog.Title class="sr-only">Workspace commands</Dialog.Title>
<Dialog.Description class="sr-only">Search commands, use arrow keys to move, and press Enter to run one.</Dialog.Description>
<Command.Root loop>
<div class="flex items-center gap-3 border-b border-ink-800 px-4">
<Search class="size-4 text-ink-400" />
<Command.Input class="h-13 w-full bg-transparent text-sm text-ink-50 outline-none placeholder:text-ink-600" placeholder="Type a command…" />
</div>
<Command.List class="max-h-72 overflow-y-auto p-2">
<Command.Viewport>
<Command.Empty class="py-10 text-center text-sm text-ink-400">No commands found.</Command.Empty>
<Command.Group>
<Command.GroupHeading class="px-3 py-2 text-xs font-medium uppercase tracking-wider text-ink-600">Quick actions</Command.GroupHeading>
<Command.GroupItems>
<Command.Item value="Open recent document" keywords={['file', 'history']} class={itemClass} onSelect={() => choose('Open recent document')}>
<FileText class="size-4" /> Open recent document
</Command.Item>
<Command.Item value="Invite a teammate" keywords={['user', 'member', 'collaborator']} class={itemClass} onSelect={() => choose('Invite a teammate')}>
<UserRound class="size-4" /> Invite a teammate
</Command.Item>
<Command.Item value="Open preferences" keywords={['settings', 'configuration']} class={itemClass} onSelect={() => choose('Open preferences')}>
<Settings class="size-4" /> Open preferences
</Command.Item>
</Command.GroupItems>
</Command.Group>
</Command.Viewport>
</Command.List>
</Command.Root>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
<p class="text-xs text-ink-400" aria-live="polite">
{lastChoice ? `Ran: ${lastChoice}` : 'Press Cmd/Ctrl+K while this demo is mounted.'}
</p>
</div>
Each Command.Item requires a unique value; optional keywords add aliases to the default scoring filter. A custom filter must return a score from 0 to 1, where 0 hides the item entirely.
The list height computed by Command.Viewport is exposed as --bits-command-list-height. Cmd/Ctrl+K is an application convention rather than a Command prop; register it only for the owning surface and remove the listener when that surface is destroyed.