CommandPalette
Keyboard-driven command palette with search, grouped results, and arrow-key navigation. Built on the Dialog primitive.
Playground
The global hotkey (Ctrl+K by default) is off here — this page already owns that key.
<script lang="ts">
import { CommandPalette, FilePlusIcon, SaveIcon, SearchIcon, SettingsIcon, LogOutIcon } from '@urbicon-ui/blocks';
let open = $state(false);
const items = [
{ label: 'New File', shortcut: 'Ctrl+N', category: 'File', icon: FilePlusIcon },
{ label: 'Save', shortcut: 'Ctrl+S', category: 'File', icon: SaveIcon },
{ label: 'Search', shortcut: 'Ctrl+F', category: 'Edit', icon: SearchIcon },
{ label: 'Settings', category: 'Navigation', icon: SettingsIcon },
{ label: 'Sign Out', category: 'Account', icon: LogOutIcon }
];
</script>
<CommandPalette
bind:open
{items}
placeholder="Type a command or search…"
/>01 Examples
Basic
<Button variant="outlined" intent="neutral" onclick={() => (basicOpen = true)}>
Open Command Palette
</Button>
<CommandPalette
bind:open={basicOpen}
items={fileCommands}
onSelect={(item) => (selectedAction = item.label)}
placeholder="Type a command or search..."
shortcut={false}
/>
{#if selectedAction}
<p class="text-text-secondary mt-3 text-sm">
Last action: <strong class="text-text-primary">{selectedAction}</strong>
</p>
{/if}Minimal (no icons, no shortcuts)
<Button variant="outlined" intent="neutral" onclick={() => (minimalOpen = true)}>
Quick Navigation
</Button>
<CommandPalette
bind:open={minimalOpen}
items={simpleCommands}
placeholder="Where do you want to go?"
shortcut={false}
/>With Disabled Items
<Button variant="outlined" intent="neutral" onclick={() => (iconsOpen = true)}>
Edit Commands
</Button>
<CommandPalette
bind:open={iconsOpen}
items={disabledCommands}
placeholder="Search edit commands..."
emptyText="No matching commands."
shortcut={false}
/>Trigger with Shortcut Badge
<button
class="border-border-default bg-surface-base text-text-tertiary hover:border-border-emphasis hover:text-text-secondary rounded-modify flex items-center gap-2 border px-4 py-2.5 text-sm shadow-[var(--blocks-shadow-sm)] transition-all"
onclick={() => (customOpen = true)}
>
<SearchIcon class="h-4 w-4" />
Search commands...
<Badge variant="outlined" intent="neutral" size="sm" class="ml-8">
<kbd class="text-3xs font-mono">Ctrl+K</kbd>
</Badge>
</button>
<CommandPalette
bind:open={customOpen}
items={fileCommands}
placeholder="Type a command..."
shortcut={false}
/>02 Customization
Styled via slotClasses
<Button variant="outlined" intent="neutral" onclick={() => (brandedOpen = true)}>
Branded Palette
</Button>
<CommandPalette
bind:open={brandedOpen}
items={simpleCommands}
placeholder="Search..."
shortcut={false}
slotClasses={{
wrapper: 'border-violet-500/30',
itemHighlighted: 'bg-violet-100 text-violet-700',
groupLabel: 'text-violet-500'
}}
/>No Footer
<Button variant="outlined" intent="neutral" onclick={() => (compactOpen = true)}>
Compact Palette
</Button>
<CommandPalette
bind:open={compactOpen}
items={disabledCommands}
placeholder="Quick search..."
showFooter={false}
shortcut={false}
/>03 Accessibility
ARIA Combobox Pattern
The search input uses role="combobox" with aria-expanded, aria-controls, and aria-activedescendant linking to the highlighted
option. Results use role="listbox" with role="option" on each item.
Keyboard
Cmd+K / Ctrl+K to open (configurable). ↑ ↓ to navigate, Enter to select, Escape to close. The highlighted item scrolls into view automatically.
Focus Management
Inherits focus trap from Dialog. The search input receives focus automatically when the
palette opens. Disabled items are skipped during keyboard navigation and have aria-disabled="true".
04 API Reference
Prop | Type | Default | Description | |
|---|---|---|---|---|
items required | CommandPaletteItem[] | — | Items to display. Grouped automatically by category. | |
class | string | — | Additional CSS classes on the root wrapper. | |
customEmpty | Snippet<[query: string]> | — | Custom empty-state renderer. Receives the current query string. | |
customItem | Snippet<[item: CommandPaletteItem, highlighted: boolean, index: number]> | — | Custom item renderer. Receives the item, whether it is highlighted, and its flat index. | |
emptyText | string | 'No results found.' | Message shown when the filter returns no results. | |
filter | (item: CommandPaletteItem, query: string) => boolean | — | Custom filter function. Receives each item and the current query.
Return true to keep the item. When omitted, a case-insensitive
label + category substring match is used. | |
onOpenChange | (open: boolean) => void | — | Fired when the open state changes (close via Escape, backdrop, or selection). | |
onSelect | (item: CommandPaletteItem) => void | — | Fired when an item is selected via click or Enter. | |
open | boolean | false | Controls visibility. Supports bind:open. | |
placeholder | string | 'Search...' | Placeholder text for the search input. | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ CommandPalette: {...} }}>.
Prefer this over class overrides when the requested look falls outside the
semantic intent palette — presets keep hover/active/dark-mode logic coherent
and make the custom look reusable across the project. | |
query | string | '' | Current search text. Supports bind:query. Reset to '' whenever the
palette opens.
Bind it to source items asynchronously — derive your results from the
query and pass them back in via items, with filter={() => true} so the
built-in label match does not filter them a second time. | |
shortcut | false | 'mod+k' | Register a global keyboard shortcut that toggles the palette. mod is
Cmd on macOS and Ctrl elsewhere. Set to false to disable. | |
showFooter | boolean | true | Show keyboard-shortcut hints in the footer. | |
size | CommandPaletteVariants['size'] | 'md' | Maximum width of the palette panel. | |
slotClasses | Partial<Record<CommandPaletteSlots, string>> | — | Per-slot class overrides. | |
unstyled | boolean | false | Strip all default styles. |
05 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
CommandPaletteItem | interface | helper | 1 | A single item in the command palette. | |
CommandPaletteProps | interface | props | 0 | Props for the CommandPalette component. | |
IconComponent | type | helper | 0 | — | |
CommandPaletteSlots | type | variant | 0 | Slot names derived from the tv() config above — single source of truth for slotClasses. | |
CommandPaletteVariants | type | variant | 0 | — | |
IconProps | interface | props | 0 | — | |
SlotNames | type | helper | 0 | Extracts the slot-name union from a slotted tv() config function — the
companion to VariantProps. The slot-mode overload returns
(props?) => { [K in keyof S]: SlotFn }, so keyof ReturnType<T> is exactly
the set of slot names a component declares in tv({ slots: … }).
Use it to type a component's slotClasses prop from the single source of
truth (its *.variants.ts) instead of hand-maintaining a parallel union
that silently drifts when a slot is added or renamed: | |
VariantProps | type | helper | 0 | — |
06 Installation
Import
import { CommandPalette } from '@urbicon-ui/blocks';
import type { CommandPaletteItem } from '@urbicon-ui/blocks';