Skip to content

CommandPalette

import { CommandPalette } from '@delightstack/components';
View code
<script>
import { CommandPalette, Button } from '@delightstack/components';
let open = $state(false);
const commands = [
{ id: 'new', title: 'New File', category: 'File', shortcut: ['Ctrl', 'N'], onselect: () => {} },
{ id: 'save', title: 'Save', category: 'File', shortcut: ['Ctrl', 'S'], onselect: () => {} },
{ id: 'copy', title: 'Copy', category: 'Edit', shortcut: ['Ctrl', 'C'], onselect: () => {} },
{ id: 'theme', title: 'Toggle Theme', category: 'View', onselect: () => {} },
];
</script>
<Button onclick={() => open = true}>Open Command Palette</Button>
<CommandPalette bind:open {commands} placeholder="Search commands..." />
<script>
import { CommandPalette } from '@delightstack/components';
let paletteOpen = $state(false);
const commands = [
{
id: 'settings',
title: 'Settings',
description: 'Open application settings',
category: 'Navigation',
onselect: () => goto('/settings')
}
];
</script>
<CommandPalette bind:open={paletteOpen} {commands} />

The palette automatically registers a global Ctrl/Cmd + K keyboard shortcut to toggle visibility.

<script>
import { CommandPalette } from '@delightstack/components';
import { goto } from '$app/navigation';
import PlusIcon from '~icons/mdi/plus';
import SettingsIcon from '~icons/mdi/cog';
import ThemeIcon from '~icons/mdi/brightness-6';
let paletteOpen = $state(false);
const commands = [
{
id: 'new-project',
title: 'New Project',
description: 'Create a new project',
category: 'Projects',
icon: PlusIcon,
shortcut: ['Ctrl', 'N'],
onselect: () => goto('/projects/new')
},
{
id: 'settings',
title: 'Settings',
description: 'Open application settings',
category: 'Navigation',
icon: SettingsIcon,
shortcut: ['Ctrl', ','],
onselect: () => goto('/settings')
},
{
id: 'theme-toggle',
title: 'Toggle Theme',
description: 'Switch between light and dark mode',
category: 'Preferences',
icon: ThemeIcon,
keywords: ['dark', 'light', 'appearance'],
onselect: () => toggleTheme()
}
];
</script>
<CommandPalette bind:open={paletteOpen} {commands} />

Use dense for tighter spacing in smaller interfaces.

View code
<CommandPalette bind:open={paletteOpen} {commands} dense />

Use comfortable for more generous spacing and larger tap targets — handy on touch interfaces or when the palette is the primary surface.

View code
<CommandPalette bind:open={paletteOpen} {commands} comfortable />

Disable grouping to show a flat list of results.

View code
<CommandPalette bind:open={paletteOpen} {commands} group_by="none" />

Track when any command is selected using the onselect prop.

<CommandPalette
bind:open={paletteOpen}
{commands}
onselect={(command) => {
analytics.track('command_used', { id: command.id });
}}
/>

When a command’s onselect returns a Promise, a spinner appears while executing. On success, the palette closes. On failure, it stays open.

const commands = [
{
id: 'sync',
title: 'Sync Data',
description: 'Synchronize with the server',
onselect: async () => {
await api.syncAll();
}
}
];
<CommandPalette
bind:open={paletteOpen}
{commands}
placeholder="Search commands, pages, or settings..."
/>
PropTypeDefaultDescription
openbooleanfalseControls visibility ($bindable())
commandsCommandOption[][]Available commands to search
placeholderstring'Type a command or search...'Input placeholder text
recent_limitnumber5Number of recent items to show when input is empty
group_by'category' | 'none''category'How to group results
densebooleanfalseCompact result item spacing
comfortablebooleanfalseRoomy result item spacing
onselect(command: CommandOption) => voidundefinedCalled when any command is selected
idstringauto-generatedElement ID
classstring''Additional CSS classes
EventDetailDescription
onselectCommandOptionFires when a command is selected, before executing command.onselect
interface CommandOption {
/** Unique identifier for the command */
id: string;
/** Display title of the command */
title: string;
/** Optional description shown below the title */
description?: string;
/** Category for grouping commands */
category?: string;
/** Optional icon component */
icon?: Component;
/** Keyboard shortcut keys to display (e.g. ['Ctrl', 'K']) */
shortcut?: string[];
/** Additional search keywords */
keywords?: string[];
/** Whether the command is disabled */
disabled?: boolean;
/** Called when the command is selected */
onselect: () => void | Promise<void>;
}
KeyAction
Ctrl/Cmd + KToggle open (global shortcut)
Arrow Up / Arrow DownNavigate results
EnterExecute selected command
EscapeClose palette

The palette uses a built-in fuzzy search engine that scores matches across title, description, category, and keywords fields. Matching characters in the title are highlighted in the results.

Scoring priority: exact match > starts with > word boundary match > contains > fuzzy character match. Title matches are weighted highest, followed by keywords, description, and category.

Recently used commands are tracked in memory and shown when the input is empty. The list is sorted by recency and limited by the recent_limit prop.

  • ARIA combobox pattern (role="combobox" on input, role="listbox" on results)
  • aria-activedescendant tracks the highlighted result
  • aria-expanded reflects whether results are visible
  • aria-autocomplete="list" on the input
  • role="option" with aria-selected and aria-disabled on each result item
  • Focus trap keeps focus within the palette while open
  • Input is always focused while the palette is open