CwSearchInput

Debounced async search with request cancellation, combobox keyboard navigation (arrows, Enter, Escape), and built-in loading, no-results and error states.

Basic

Try “berry”, or “xyz” to see the no-results state.

Last event: none yet

CwSearchInput example Copy code
<CwSearchInput
	label="Search fruits"
	placeholder="Type at least 3 chars…"
	fetchSuggestions={fetchSuggestions}
	bind:value={value}
/>

Sizes

States

The request always fails in this example.

Pick a fruit to continue

API

Usage and API reference

CwSearchInput is for async suggestion search. It handles debouncing, cancellation, keyboard navigation, and selected-value binding so you can focus on fetching and mapping results.

How to think about it

  1. Return suggestions from `fetchSuggestions` The callback receives both the query string and an `AbortSignal`. Respect the signal so old requests can be cancelled cleanly.
  2. Map objects into labels and values For simple string arrays the defaults are enough. For object results, provide `mapSuggestionToLabel` and `mapSuggestionToValue`.
  3. Separate typing from selection Use `oninput` to respond to free typing and `onselect` when the user actually chooses a suggestion.

Props and callbacks

Native field props such as `name`, `required`, `autocomplete`, and `class` are also supported.

APITypeDetails
value

Default: ''

stringCurrent text value. Bind this when the parent owns the field state.
fetchSuggestions
(query: string, signal: AbortSignal) => Promise<unknown[]>Async loader for the suggestion list.
minChars

Default: 3

numberMinimum characters before fetching suggestions.
debounceMs

Default: 250

numberDelay before triggering `fetchSuggestions`.
mapSuggestionToLabel
(item: unknown) => stringConverts a result item into visible list text.
mapSuggestionToValue
(item: unknown) => stringConverts a result item into the bound field value.
label / placeholder
stringOptional field label and empty-state prompt.
clearable

Default: true

booleanShows a clear button when the field has content.
disabled

Default: false

booleanDisables typing and selection.
oninput
(value: string) => voidRuns whenever the raw text changes.
onselect
(value: string, item: unknown) => voidRuns when the user chooses a suggestion.
size

Default: 'md'

'sm' | 'md' | 'lg'Control height and font scale, shared with CwButton and every other form control.
hint
stringHelper text under the field. Replaced by `error` when both are set.
error
stringError message under the field. Sets `aria-invalid` and links the text with `aria-describedby`.
labels
CwSearchInput labelsStrings for the no-results and error states.

Copy-paste examples

Each snippet is a complete, working example you can paste into a Svelte 5 component.

Search over a simple string list

The default mapping helpers are enough when your suggestions are already strings.

Search over a simple string list Copy code
<script lang="ts">
	const crops = ['Tomato', 'Pepper', 'Lettuce', 'Basil'];
	let value = $state('');

	async function fetchSuggestions(query: string, signal: AbortSignal) {
		await new Promise((resolve) => setTimeout(resolve, 250));
		if (signal.aborted) throw new DOMException('Aborted', 'AbortError');
		return crops.filter((crop) => crop.toLowerCase().includes(query.toLowerCase()));
	}
</script>

<CwSearchInput
	label="Search crops"
	minChars={1}
	fetchSuggestions={fetchSuggestions}
	bind:value={value}
/>
Search object results with custom mapping

Use this pattern when the API returns structured objects.

Search object results with custom mapping Copy code
<script lang="ts">
	let selectedId = $state('');

	async function fetchDevices(query: string, signal: AbortSignal) {
		const response = await fetch('/api/devices?q=' + encodeURIComponent(query), { signal });
		return await response.json();
	}
</script>

<CwSearchInput
	label="Search devices"
	name="device"
	placeholder="Type device name or serial..."
	fetchSuggestions={fetchDevices}
	mapSuggestionToLabel={(item) => `${item.name} (${item.serial})`}
	mapSuggestionToValue={(item) => item.id}
	oninput={(value) => console.log('Typing:', value)}
	onselect={(value, item) => {
		selectedId = value;
		console.log('Selected device:', item);
	}}
/>