Command Palette
blocklayoutHeader-grade search/command palette. Bundles the slim trigger button (with platform-aware ⌘K/Ctrl-K kbd hint) AND the modal CommandDialog into one block; consumers drop it once and the global keyboard shortcut wires itself. Takes a `groups` array of `{ heading, items: [{ label, hint, icon, onSelect }] }` and an `onSelect` callback. `showTrigger=false` hides the inline button so the consumer can fire it from elsewhere via `bind:open`.
Installation
$pnpm dlx shadcn-svelte@latest add https://uipkge.dev/r/svelte/command-palette.json$npx shadcn-svelte@latest add https://uipkge.dev/r/svelte/command-palette.json$yarn dlx shadcn-svelte@latest add https://uipkge.dev/r/svelte/command-palette.json$bunx shadcn-svelte@latest add https://uipkge.dev/r/svelte/command-palette.jsonManifest:
https://uipkge.dev/r/svelte/command-palette.jsonInstalls to:src/lib/components/blocks/command-palette/Variants
Loading interactive previews…
Loading interactive previews…
npm dependencies
Includes
Used by
Files installed (1)
src/lib/components/blocks/command-palette/CommandPalette.svelte4.2 kB
<script lang="ts" module> import type { Component } from 'svelte' export interface CommandPaletteItem { label: string value?: string hint?: string icon?: Component onSelect?: () => void } export interface CommandPaletteGroup { heading: string items: CommandPaletteItem[] } export interface CommandPaletteProps { groups?: CommandPaletteGroup[] placeholder?: string triggerLabel?: string showTrigger?: boolean /** Controlled open state (`bind:open`) — the Svelte counterpart of the Vue twin's exposed show()/hide()/toggle(). */ open?: boolean onOpenChange?: (open: boolean) => void onSelect?: (item: CommandPaletteItem) => void } </script> <script lang="ts"> import { onMount } from 'svelte' import { FileText, Inbox, KanbanSquare, LayoutDashboard, Search, Settings, Users } from '@lucide/svelte' import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, } from '$lib/components/ui/command' let { groups = [ { heading: 'Navigate', items: [ { label: 'Dashboard', hint: '/dashboard', icon: LayoutDashboard }, { label: 'Inbox', hint: '/inbox', icon: Inbox }, { label: 'Kanban', hint: '/kanban', icon: KanbanSquare }, { label: 'Team', hint: '/team', icon: Users }, ], }, { heading: 'Settings', items: [ { label: 'Profile', hint: '/profile', icon: FileText }, { label: 'Settings', hint: '/settings', icon: Settings }, ], }, ], placeholder = 'Search pages, commands…', triggerLabel = 'Search pages, commands…', showTrigger = true, open = $bindable(false), onOpenChange, onSelect, }: CommandPaletteProps = $props() function setOpen(next: boolean) { open = next onOpenChange?.(next) } function pick(item: CommandPaletteItem) { setOpen(false) item.onSelect?.() onSelect?.(item) } function onKeydown(e: KeyboardEvent) { if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { e.preventDefault() setOpen(!open) } } const triggerShortcut = $derived.by(() => { if (typeof navigator === 'undefined') return '⌘K' return /Mac|iPhone|iPad/i.test(navigator.platform) ? '⌘K' : 'Ctrl K' }) onMount(() => { window.addEventListener('keydown', onKeydown) return () => window.removeEventListener('keydown', onKeydown) }) </script> {#if showTrigger} <button data-slot="command-palette" type="button" class="bg-secondary/50 hover:bg-secondary text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 relative hidden h-8 w-full items-center gap-2 rounded-lg border border-transparent px-2.5 text-sm shadow-none transition-colors outline-none focus-visible:ring-[3px] sm:flex md:w-56 lg:w-80" aria-label="Open command palette" onclick={() => setOpen(true)} > <Search class="size-3.5 shrink-0" /> <span class="flex-1 truncate text-left">{triggerLabel}</span> <kbd class="bg-muted/80 text-muted-foreground pointer-events-none flex h-5 items-center justify-center rounded-md border px-1.5 font-mono text-xs font-medium" > <span>{triggerShortcut}</span> </kbd> </button> {/if} <CommandDialog bind:open {onOpenChange} title="Command palette" description="Search pages and run commands"> <CommandInput {placeholder} /> <CommandList class="max-h-[30rem]"> <CommandEmpty>No matches.</CommandEmpty> {#each groups as group, gi (group.heading)} <CommandGroup heading={group.heading}> {#each group.items as item (`${group.heading}-${item.label}`)} {@const ItemIcon = item.icon} <CommandItem value={`${group.heading} ${item.label} ${item.hint ?? ''}`} onSelect={() => pick(item)}> {#if ItemIcon} <ItemIcon class="size-4" /> {/if} <span>{item.label}</span> {#if item.hint} <CommandShortcut class="text-muted-foreground/70">{item.hint}</CommandShortcut> {/if} </CommandItem> {/each} </CommandGroup> {#if gi < groups.length - 1} <CommandSeparator /> {/if} {/each} </CommandList> </CommandDialog>
Raw manifest:https://uipkge.dev/r/svelte/command-palette.json