Search

Search input with suggestion dropdown, grouped results, matched-text highlight, recent searches, async loading state, ⌘K command palette mode, and expandable animation.

Import

ts
import Search from '$lib/components/Search.svelte';
import type { SearchItem } from '$lib/components/Search.svelte';

Usage

svelte
<script lang="ts">
  let value = $state('');
</script>

<Search bind:value placeholder="Search..." />

Sizes

Three size presets: sm, md (default), lg.

svelte
<Search size="sm" placeholder="Small..." />
<Search size="md" placeholder="Medium..." />
<Search size="lg" placeholder="Large..." />

Expandable

Set expandable to collapse the input to a search icon; it expands with a spring animation on click and closes on blur.

svelte
<!-- Collapses to an icon; expands on click with a spring animation -->
<Search expandable size="sm" placeholder="Search..." recentKey="exp-sm" />

Suggestions, grouped results & highlight

Pass items to show a suggestion dropdown. Results are grouped by category, matched text is highlighted, badges are shown. Recent searches are persisted via recentKey. Try: "svelte", "frontend", "#svelte"

svelte
<script lang="ts">
  let q = $state('');
  const items: SearchItem[] = [
    { id: 1, label: 'Svelte 5 Runes', description: '12:34 · 284K views', category: 'Videos', badge: 'Trending' },
    { id: 2, label: 'CSS Grid',        description: '24:10 · 112K views', category: 'Videos' },
  ];
  const results = $derived(q.trim()
    ? items.filter(i => i.label.toLowerCase().includes(q.toLowerCase()))
    : []);
</script>

<Search
  bind:value={q}
  placeholder="Search videos..."
  items={results}
  recentKey="search-demo"
  onSelect={(item) => console.log(item)}
/>

Async search

Simulated API call with 800ms delay — shows a spinner and loading dots. Pass loading=true while your request is in flight.

svelte
<script lang="ts">
  let q        = $state('');
  let loading  = $state(false);
  let results  = $state<SearchItem[]>([]);
  let timer: ReturnType<typeof setTimeout>;

  function onSearch(query: string) {
    clearTimeout(timer);
    if (!query) { results = []; return; }
    loading = true;
    timer = setTimeout(async () => {
      results = await fetchResults(query);
      loading = false;
    }, 400);
  }
</script>

<Search bind:value={q} {items} {loading} onSearch={onSearch} recentKey="async" />

Command palette (⌘K)

Set command to register a global keyboard shortcut. Focus with ⌘K or Ctrl+K. Try: "button", "slider", "dashboard"

⌘K
svelte
<Search
  bind:value={q}
  placeholder="Search components or pages..."
  items={results}
  command
  shortcut="⌘K"
  size="lg"
  recentKey="cmdk"
/>

API Reference

PropTypeDefaultDescription
value string''Bindable input value.
placeholder string'Search...'Input placeholder text.
size 'sm' | 'md' | 'lg''md'Controls height and font size.
items SearchItem[]Suggestion items to display in the dropdown.
loading booleanfalseShows a spinner and loading dots in the dropdown.
debounce numberMilliseconds to debounce the onSearch callback.
maxRecent numberMax recent searches to store.
recentKey stringlocalStorage key for persisting recent searches. Unique per instance.
command booleanfalseEnables ⌘K / Ctrl+K global keyboard shortcut to focus the input.
shortcut stringShortcut badge label shown inside the input (e.g. "⌘K").
emptyText stringText shown when items is empty and a query is active.
expandable booleanfalseCollapses to an icon; expands on click with a spring animation.
onSearch (q: string) => voidCallback fired on every input change (after optional debounce).