Remote combo box

SvComboBox with a remote dataSource: debounced server search (loadOptions), a loading state, latest-response-wins race handling and a type-to-search hint. Local filtering off.

A live, editable Svelte 5 component example from the SvGrid gallery (Selection). Read the SvGrid UI component docs for the full API.

About this example

SvComboBox with a remote data source in Svelte 5: options are fetched from a server as you type through loadOptions, debounced, with a loading state, a type-to-search hint until minLength characters are entered, and latest-response-wins race handling so a slow older query never overwrites newer results. Local filtering is off and the server's results are shown as they arrive.

SvComboBox with a remote data source: options are fetched from a server as you type (debounced), with a loading state and a "type to search" hint. Local filtering is off - the server's results are shown as-is.

Imports, features and API used

Imports: @svgrid/grid

Frequently asked questions

What does loadOptions receive and return?

It receives the typed query and returns a promise of options. The combo box debounces calls, shows a spinner while pending and tags each request with a sequence number so only the latest response is applied.

Why is local filtering off?

The server already filtered; filtering again on the client could hide results whose label does not contain the query, for example matches on a description.

How do I avoid searching on one character?

Set minLength. Below it the panel shows the type-to-search hint and no request is sent.

Related documentation

Related articles

Source code (329-combobox-remote.svelte)

<script lang="ts">
  /**
   * SvComboBox with a remote data source: options are fetched from a server as
   * you type (debounced), with a loading state and a "type to search" hint.
   * Local filtering is off - the server's results are shown as-is.
   */
  import { SvComboBox } from '@svgrid/grid'
  import type { ListOption } from '@svgrid/grid'

  // A stand-in "server": a big dataset queried with a simulated latency.
  const universe: ListOption[] = Array.from({ length: 5000 }, (_, i) => ({
    value: i,
    label: `Repository ${i + 1} - ${['core', 'ui', 'api', 'docs', 'infra'][i % 5]}`,
  }))
  function searchServer(query: string): Promise<ListOption[]> {
    const q = query.toLowerCase()
    return new Promise((resolve) =>
      setTimeout(() => resolve(universe.filter((o) => o.label.toLowerCase().includes(q)).slice(0, 25)), 500),
    )
  }

  let repo = $state<string | number | null>(null)
  let calls = $state(0)
  async function loadOptions(query: string) {
    calls += 1
    return searchServer(query)
  }
</script>

<div class="wrap">
  <header>
    <h2>Remote combo box</h2>
    <p>Type to search a 5,000-item "server". Requests are <strong>debounced</strong> (250ms), show a <strong>loading</strong> state, and only the latest response wins (no race). Local filtering is disabled.</p>
  </header>

  <div class="field">
    <SvComboBox
      value={repo}
      {loadOptions}
      minLength={2}
      label="Repository"
      hint="At least 2 characters"
      placeholder="Search repositories..."
      onChange={(v) => (repo = v)}
    />
    <p class="out">{repo != null ? `Selected: ${universe.find((o) => o.value === repo)?.label}` : 'Nothing selected'} <span class="muted">&middot; {calls} server calls</span></p>
  </div>
</div>

<style>
  .wrap { padding: 20px; max-width: 460px; display: flex; flex-direction: column; gap: 16px; }
  header h2 { margin: 0 0 4px; font-size: 20px; font-weight: 700; }
  header p { margin: 0; color: var(--sg-muted, #64748b); font-size: 13.5px; line-height: 1.5; }
  .field { display: flex; flex-direction: column; gap: 8px; }
  .out { margin: 0; font-size: 13px; }
  .muted { color: var(--sg-muted, #94a3b8); }
</style>

View this example on GitHub

More Selection examples

  • Selection controls - List and overlay pickers: SvListBox (inline multi-select), SvDropDownList, SvComboBox (type to filter), SvAutoComplete (free text + suggestions), SvTagsInput (chips) and SvCountryInput (searchable, flags). Every popover portals out of the scroll container. Grid cell editors, standalone too.
  • List box - SvListBox: an assignee picker - inline multi-select with grouped options (departments), roving keyboard + type-ahead and a live summary.
  • Combo box - SvComboBox: a form field - type to filter a grouped, portalled list; the value must come from the list. With label + required validation.
  • Drop-down list - SvDropDownList: a status / priority toolbar with grouped options, type-ahead and animated portalled panels driving a live issue list.
  • Autocomplete - SvAutoComplete: a free-text search field with live suggestion shortcuts (any value accepted) - city search and a filter-syntax helper.