Skip to main content
Urbicon UI
source

CommandPalette

Keyboard-driven command palette with search, grouped results, and arrow-key navigation. Built on the Dialog primitive.

Playground

The global hotkey (Cmd+K / Ctrl+K by default) is turned off in this demo, because the docs site already uses that key for its search.

Size
<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

Simple command palette with categories and keyboard shortcuts.
<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)

A list with no icons and no shortcut hints.
<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

Set disabled: true on an item to show it greyed out and skip it in keyboard navigation.
<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

A search-bar styled trigger with a Ctrl+K badge, instead of a plain button.
<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

Override specific slots for a branded appearance.
<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'
  }}
/>

Custom Rows

The customItem snippet draws each row's contents — content only, nothing focusable — while the option container (role, id, highlight, hover and click) stays with the palette. Here the highlighted argument puts an Enter hint on the active row.
<Button variant="outlined" intent="neutral" onclick={() => (customRowOpen = true)}>
  Custom Rows
</Button>
<CommandPalette
  bind:open={customRowOpen}
  items={simpleCommands}
  placeholder="Search..."
  shortcut={false}
>
  {#snippet customItem(item, highlighted)}
    <span class="flex min-w-0 flex-1 items-center justify-between gap-2">
      <span class="truncate">{item.label}</span>
      {#if highlighted}
        <Badge purpose="tag" variant="soft" size="xs">Enter</Badge>
      {/if}
    </span>
  {/snippet}
</CommandPalette>

No Footer

Hide the keyboard hints footer for a more compact look.
<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 — the list wraps at both ends — Home / End for the first and last item, 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

17 props
17 props 1 required
Prop
Type
Default
Description

05 Types

Local type definitions used by this component.

8 types
Name
Kind
Category
Used by
Description

06 Installation

Import

import { CommandPalette } from '@urbicon-ui/blocks';
import type { CommandPaletteItem } from '@urbicon-ui/blocks';