Kumo Svelte
  • Home
  • Installation
  • Colors
  • Accessibility
  • Changelog
  • Autocomplete
  • Badge
  • Banner
  • Breadcrumbs
  • Button
  • Checkbox
  • Clipboard Text
  • Cloudflare Logo
  • CodeHighlighted
  • Collapsible
  • Combobox
  • Command Palette
  • Date Picker
  • Dialog
  • Dropdown
  • Empty
  • Flow
  • Grid
  • Input
  • InputArea
  • InputGroup
  • Label
  • Layer Card
  • Link
  • Loader
  • MenuBar
  • Meter
  • Pagination
  • Popover
  • Radio
  • Select
  • Sensitive Input
  • Sidebar
  • Skeleton Line
  • Switch
  • Table
  • Table of Contents
  • Tabs
  • Text
  • Toast
  • Toolbar
  • Tooltip
  • Charts
  • Colors
  • Timeseries
  • Maps
  • Sankey
  • Custom Chart
  • Page Header
  • Resource List
  • Delete Resource
CommandPalette
kumo-svelte

CommandPalette

A composable command palette component for search and command dialogs.

<script lang="ts">
  import { Button } from "kumo-svelte/components/button";
  import * as CommandPalette from "kumo-svelte/components/command-palette";
  import ChartLineIcon from "phosphor-svelte/lib/ChartLineIcon";
  import FolderIcon from "phosphor-svelte/lib/FolderIcon";
  import GearIcon from "phosphor-svelte/lib/GearIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import MagnifyingGlassIcon from "phosphor-svelte/lib/MagnifyingGlassIcon";
  import UsersIcon from "phosphor-svelte/lib/UsersIcon";

  const groups = [
    {
      label: "Commands",
      items: [
        { value: "create-new-project", title: "Create New Project", icon: FolderIcon },
        { value: "open-settings", title: "Open Settings", icon: GearIcon },
        { value: "search-files", title: "Search Files", icon: MagnifyingGlassIcon },
      ],
    },
    {
      label: "Pages",
      items: [
        { value: "home", title: "Home", icon: HouseIcon },
        { value: "dashboard", title: "Dashboard", icon: ChartLineIcon },
        { value: "users", title: "Users", icon: UsersIcon },
      ],
    },
  ];

  let open = $state(false);
  let search = $state("");
  let selectedItem = $state<string | null>(null);

  function handleSelect(title: string) {
    selectedItem = title;
    search = "";
    open = false;
  }
</script>

<div class="flex flex-col items-start gap-4">
  <Button onclick={() => (open = true)}>Open Command Palette</Button>
  {#if selectedItem}
    <p class="text-sm text-kumo-subtle">
      Last selected: <span class="text-kumo-default">{selectedItem}</span>
    </p>
  {/if}

  <CommandPalette.Dialog bind:open>
    <CommandPalette.Root bind:value={search}>
      <CommandPalette.Input placeholder="Type a command or search..." />
      <CommandPalette.List>
        {#each groups as group}
          <CommandPalette.Group value={group.label}>
            <CommandPalette.GroupLabel>{group.label}</CommandPalette.GroupLabel>
            {#each group.items as item}
              {@const Icon = item.icon}
              <CommandPalette.Item value={item.title} onSelect={() => handleSelect(item.title)}>
                <span class="flex items-center gap-3">
                  <span class="text-kumo-subtle"><Icon size={16} /></span>
                  <span>{item.title}</span>
                </span>
              </CommandPalette.Item>
            {/each}
          </CommandPalette.Group>
        {/each}
        <CommandPalette.Empty>No commands found</CommandPalette.Empty>
      </CommandPalette.List>
      <CommandPalette.Footer>
        <span class="flex items-center gap-2">
          <kbd class="rounded border border-kumo-hairline bg-kumo-base px-1.5 py-0.5 text-[10px]">↑↓</kbd>
          <span>Navigate</span>
        </span>
        <span class="flex items-center gap-2">
          <kbd class="rounded border border-kumo-hairline bg-kumo-base px-1.5 py-0.5 text-[10px]">↵</kbd>
          <span>Select</span>
        </span>
      </CommandPalette.Footer>
    </CommandPalette.Root>
  </CommandPalette.Dialog>
</div>

Import

import * as CommandPalette from "kumo-svelte/components/command-palette";

Usage

CommandPalette is built on Bits UI’s Command primitive. Use CommandPalette.Dialog when the palette should open in a modal dialog, or use CommandPalette directly for an inline command surface.

<script lang="ts">
  import { Button } from "kumo-svelte/components/button";
  import * as CommandPalette from "kumo-svelte/components/command-palette";
  const items = ["Create Project", "Open Settings"];
  let open = $state(false);
  let search = $state("");
</script>

<Button onclick={() => (open = true)}>Open</Button>

<CommandPalette.Dialog bind:open>
  <CommandPalette.Root bind:value={search}>
    <CommandPalette.Input placeholder="Search..." />
    <CommandPalette.List>
      {#each items as item}
        <CommandPalette.Item value={item} onSelect={() => (open = false)}>
          {item}
        </CommandPalette.Item>
      {/each}
      <CommandPalette.Empty>No results</CommandPalette.Empty>
    </CommandPalette.List>
  </CommandPalette.Root>
</CommandPalette.Dialog>

Keyboard Navigation

Built-in keyboard navigation is provided automatically by the Command primitive:

KeysDescription
↑ / ↓Move highlight between items.
EnterSelect highlighted item.
EscapeClose the dialog when used inside CommandPalette.Dialog.

Examples

With Grouped Items

Group related commands together with labels.

<script lang="ts">
  import { Button } from "kumo-svelte/components/button";
  import * as CommandPalette from "kumo-svelte/components/command-palette";
  import ChartLineIcon from "phosphor-svelte/lib/ChartLineIcon";
  import FolderIcon from "phosphor-svelte/lib/FolderIcon";
  import GearIcon from "phosphor-svelte/lib/GearIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import MagnifyingGlassIcon from "phosphor-svelte/lib/MagnifyingGlassIcon";
  import UsersIcon from "phosphor-svelte/lib/UsersIcon";

  const groups = [
    {
      label: "Commands",
      items: [
        { value: "create-new-project", title: "Create New Project", icon: FolderIcon },
        { value: "open-settings", title: "Open Settings", icon: GearIcon },
        { value: "search-files", title: "Search Files", icon: MagnifyingGlassIcon },
      ],
    },
    {
      label: "Pages",
      items: [
        { value: "home", title: "Home", icon: HouseIcon },
        { value: "dashboard", title: "Dashboard", icon: ChartLineIcon },
        { value: "users", title: "Users", icon: UsersIcon },
      ],
    },
  ];

  let open = $state(false);
  let search = $state("");
  let selectedItem = $state<string | null>(null);

  function handleSelect(title: string) {
    selectedItem = title;
    search = "";
    open = false;
  }
</script>

<div class="flex flex-col items-start gap-4">
  <Button onclick={() => (open = true)}>Open Command Palette</Button>
  {#if selectedItem}
    <p class="text-sm text-kumo-subtle">
      Last selected: <span class="text-kumo-default">{selectedItem}</span>
    </p>
  {/if}

  <CommandPalette.Dialog bind:open>
    <CommandPalette.Root bind:value={search}>
      <CommandPalette.Input placeholder="Type a command or search..." />
      <CommandPalette.List>
        {#each groups as group}
          <CommandPalette.Group value={group.label}>
            <CommandPalette.GroupLabel>{group.label}</CommandPalette.GroupLabel>
            {#each group.items as item}
              {@const Icon = item.icon}
              <CommandPalette.Item value={item.title} onSelect={() => handleSelect(item.title)}>
                <span class="flex items-center gap-3">
                  <span class="text-kumo-subtle"><Icon size={16} /></span>
                  <span>{item.title}</span>
                </span>
              </CommandPalette.Item>
            {/each}
          </CommandPalette.Group>
        {/each}
        <CommandPalette.Empty>No commands found</CommandPalette.Empty>
      </CommandPalette.List>
      <CommandPalette.Footer>
        <span class="flex items-center gap-2">
          <kbd class="rounded border border-kumo-hairline bg-kumo-base px-1.5 py-0.5 text-[10px]">↑↓</kbd>
          <span>Navigate</span>
        </span>
        <span class="flex items-center gap-2">
          <kbd class="rounded border border-kumo-hairline bg-kumo-base px-1.5 py-0.5 text-[10px]">↵</kbd>
          <span>Select</span>
        </span>
      </CommandPalette.Footer>
    </CommandPalette.Root>
  </CommandPalette.Dialog>
</div>

Simple Flat List

For simpler use cases, use a flat array of items without grouping.

<script lang="ts">
  import { Button } from "kumo-svelte/components/button";
  import * as CommandPalette from "kumo-svelte/components/command-palette";
  const items = ["Copy", "Paste", "Cut", "Delete", "Select All"];
  let open = $state(false);
  let search = $state("");
</script>

<div>
  <Button onclick={() => (open = true)}>Open Simple Palette</Button>

  <CommandPalette.Dialog bind:open>
    <CommandPalette.Root bind:value={search}>
      <CommandPalette.Input placeholder="Search actions..." />
      <CommandPalette.List>
        {#each items as item}
          <CommandPalette.Item value={item} onSelect={() => (open = false)}>
            {item}
          </CommandPalette.Item>
        {/each}
        <CommandPalette.Empty>No actions found</CommandPalette.Empty>
      </CommandPalette.List>
    </CommandPalette.Root>
  </CommandPalette.Dialog>
</div>

Loading State

Show a loading spinner while fetching results.

<script lang="ts">
  import { Button } from "kumo-svelte/components/button";
  import * as CommandPalette from "kumo-svelte/components/command-palette";
  const groups = [
    { label: "Commands", items: ["Create New Project", "Open Settings", "Search Files"] },
    { label: "Pages", items: ["Home", "Dashboard", "Users"] },
  ];

  let open = $state(false);
  let loading = $state(false);
  let search = $state("");

  function handleOpen() {
    open = true;
    loading = true;
    setTimeout(() => {
      loading = false;
    }, 1500);
  }
</script>

<div>
  <Button onclick={handleOpen}>Open with Loading</Button>

  <CommandPalette.Dialog bind:open>
    <CommandPalette.Root bind:value={search}>
      <CommandPalette.Input placeholder="Search..." />
      <CommandPalette.List>
        {#if loading}
          <CommandPalette.Loading />
        {:else}
          {#each groups as group}
            <CommandPalette.Group value={group.label}>
              <CommandPalette.GroupLabel>{group.label}</CommandPalette.GroupLabel>
              {#each group.items as item}
                <CommandPalette.Item value={item} onSelect={() => (open = false)}>
                  {item}
                </CommandPalette.Item>
              {/each}
            </CommandPalette.Group>
          {/each}
          <CommandPalette.Empty>No results found</CommandPalette.Empty>
        {/if}
      </CommandPalette.List>
    </CommandPalette.Root>
  </CommandPalette.Dialog>
</div>

Disabling Browser Autocomplete

Pass standard HTML input attributes like autocomplete to suppress browser autocomplete overlays.

<script lang="ts">
  import { Button } from "kumo-svelte/components/button";
  import * as CommandPalette from "kumo-svelte/components/command-palette";
  const groups = [
    { label: "Commands", items: ["Create New Project", "Open Settings", "Search Files"] },
    { label: "Pages", items: ["Home", "Dashboard", "Users"] },
  ];

  let open = $state(false);
  let search = $state("");
</script>

<div class="flex flex-col items-start gap-4">
  <Button onclick={() => (open = true)}>Open Palette (No Autocomplete)</Button>

  <CommandPalette.Dialog bind:open>
    <CommandPalette.Root bind:value={search}>
      <CommandPalette.Input placeholder="Search commands..." autocomplete="off" />
      <CommandPalette.List>
        {#each groups as group}
          <CommandPalette.Group value={group.label}>
            <CommandPalette.GroupLabel>{group.label}</CommandPalette.GroupLabel>
            {#each group.items as item}
              <CommandPalette.Item
                value={item}
                onSelect={() => {
                  search = "";
                  open = false;
                }}
              >
                {item}
              </CommandPalette.Item>
            {/each}
          </CommandPalette.Group>
        {/each}
        <CommandPalette.Empty>No commands found</CommandPalette.Empty>
      </CommandPalette.List>
    </CommandPalette.Root>
  </CommandPalette.Dialog>
</div>

ResultItem with Breadcrumbs

Use CommandPalette.ResultItem for rich items with breadcrumbs, icons, and optional text highlighting.

<script lang="ts">
  import { Button } from "kumo-svelte/components/button";
  import * as CommandPalette from "kumo-svelte/components/command-palette";
  import FileIcon from "phosphor-svelte/lib/FileIcon";

  const results = [
    { value: "button", title: "Button", breadcrumbs: ["Components"] },
    { value: "dialog", title: "Dialog", breadcrumbs: ["Components"] },
    { value: "page-header", title: "Page Header", breadcrumbs: ["Blocks"] },
  ];

  let open = $state(false);
  let search = $state("");
</script>

{#snippet fileIcon()}
  <FileIcon size={16} />
{/snippet}

<div>
  <Button onclick={() => (open = true)}>Open with ResultItem</Button>

  <CommandPalette.Dialog bind:open>
    <CommandPalette.Root bind:value={search}>
      <CommandPalette.Input placeholder="Search documentation..." />
      <CommandPalette.List>
        {#each results as result}
          <CommandPalette.ResultItem
            value={result.title}
            title={result.title}
            breadcrumbs={result.breadcrumbs}
            icon={fileIcon}
            onSelect={() => (open = false)}
          />
        {/each}
        <CommandPalette.Empty>No pages found</CommandPalette.Empty>
      </CommandPalette.List>
      <CommandPalette.Footer>
        <span class="flex items-center gap-2">
          <kbd class="rounded border border-kumo-hairline bg-kumo-base px-1.5 py-0.5 text-[10px]">↑↓</kbd>
          <span>Navigate</span>
        </span>
        <span class="flex items-center gap-2">
          <kbd class="rounded border border-kumo-hairline bg-kumo-base px-1.5 py-0.5 text-[10px]">⌘↵</kbd>
          <span>Open in new tab</span>
        </span>
      </CommandPalette.Footer>
    </CommandPalette.Root>
  </CommandPalette.Dialog>
</div>

API Reference

CommandPalette

PropTypeDefault
childrenSnippet—
classstring—
filter(value: string, search: string, keywords?: string[]) => number—
labelstring"Command palette"
loopboolean—
onValueChange(value: string) => void—
shouldFilterboolean—
valuestring""

CommandPalette.Dialog

PropTypeDefault
childrenSnippet—
classstring—
onOpenChange(open: boolean) => void—
openbooleanfalse

CommandPalette.Input

PropTypeDefault
classstring—
leadingSnippet—
trailingSnippet—

CommandPalette.List

PropTypeDefault
childrenSnippet—
classstring—

CommandPalette.Group

PropTypeDefault
childrenSnippet—
classstring—
forceMountboolean—
valuestring—

CommandPalette.GroupLabel

PropTypeDefault
childrenSnippet—
classstring—

CommandPalette.Item

PropTypeDefault
childrenSnippet—
classstring—
disabledbooleanfalse
forceMountboolean—
keywordsstring[]—
onSelect() => void—
valuestring—

CommandPalette.ResultItem

PropTypeDefault
breadcrumbHighlightsHighlightRange[][]—
breadcrumbsstring[][]
descriptionstring—
externalbooleanfalse
iconSnippet—
nonInteractivebooleanfalse
onSelect() => void—
showArrowbooleantrue
title*string—
titleHighlightsHighlightRange[]—
valuestring—

HighlightedText

PropTypeDefault
classstring—
highlightsHighlightRange[][]
text*string—

CommandPalette.Empty

PropTypeDefault
childrenSnippet—
forceMountboolean—

CommandPalette.Loading

PropTypeDefault
childrenSnippet—
progressnumber—

CommandPalette.Footer

PropTypeDefault
childrenSnippet—
classstring—

CommandPalette.Separator

PropTypeDefault
classstring—
forceMountboolean—

On this page

  • Import
  • Usage
  • Keyboard Navigation
  • Examples
    • With Grouped Items
    • Simple Flat List
    • Loading State
    • Disabling Browser Autocomplete
    • ResultItem with Breadcrumbs
  • API Reference
    • CommandPalette
    • CommandPalette.Dialog
    • CommandPalette.Input
    • CommandPalette.List
    • CommandPalette.Group
    • CommandPalette.GroupLabel
    • CommandPalette.Item
    • CommandPalette.ResultItem
    • HighlightedText
    • CommandPalette.Empty
    • CommandPalette.Loading
    • CommandPalette.Footer
    • CommandPalette.Separator