# SearchInput

> A pill-shaped search field with a leading icon, a clear button once there's something to clear, and optional debounced search.

- Package: `fancy-ui-svelte` (Svelte 5 + Tailwind CSS v4)
- Category: Forms
- Tags: search, input, form, debounce, filter
- Docs: https://fancy-ui.rama.app/docs/components/search-input
- Source: https://github.com/RamaHerbin/fancy-ui/tree/main/src/lib/fancy-ui/search-input

## Import

```svelte
<script lang="ts">
  import { SearchInput } from 'fancy-ui-svelte';
</script>
```

Setup (once per project): `npm install fancy-ui-svelte`, then add `@import "fancy-ui-svelte/tailwind.css";` after `@import "tailwindcss";` in your main CSS file. Full guide: https://fancy-ui.rama.app/llms.txt

React: `import { SearchInput } from 'fancy-ui-react';` (same prop names; `class` becomes `className`, snippets become `ReactNode`).

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | `""` | Current value; bindable |
| `onValueChange` | `(value: string) => void` | — | Called with the new value on every input event |
| `onSearch` | `(value: string) => void` | — | Fired on Enter, and on debounced settle when debounceMs is set |
| `placeholder` | `string` | `"Search"` | Shown while the field is empty |
| `debounceMs` | `number` | `0` | Delay before a settled value fires onSearch on its own; 0 disables debouncing |
| `disabled` | `boolean` | `false` | Blocks focus and typing; excluded from form submission |
| `readonly` | `boolean` | `false` | Blocks typing and clearing but stays focusable and is still submitted |
| `required` | `boolean` | `false` | Native required |
| `invalid` | `boolean` | `false` | Drives the error border and aria-invalid |
| `id` | `string` | — | Element id |
| `name` | `string` | — | Native name, read on form submission |
| `label` | `string` | — | Accessible name — for a control with no visible Label next to it |
| `clearable` | `boolean` | `true` | Renders a clear button once there is something to clear |
| `class` | `string` | — | Additional CSS classes, merged onto the field surface |
| `ref` | `HTMLInputElement \| null` | `null` | Bindable element reference |
| `sound` | `boolean` | `false` | Plays the press cue when the field is cleared, once the user has enabled sound |

## Examples

### Basic Usage

```svelte
<script lang="ts">
	import { SearchInput } from "fancy-ui-svelte";

	let query = $state("");
</script>

<SearchInput
	sound
	bind:value={query}
	placeholder="Search components"
	onSearch={(v) => console.log(v)}
/>
```

### Debounced

onSearch fires 300ms after typing settles instead of on every keystroke.

```svelte
<script lang="ts">
	import { SearchInput } from "fancy-ui-svelte";

	let query = $state("");
	let lastSearch = $state("");
</script>

<div class="flex w-full max-w-sm flex-col gap-2">
	<SearchInput
		sound
		bind:value={query}
		placeholder="Type to filter…"
		debounceMs={300}
		onSearch={(v) => (lastSearch = v)}
	/>
	<p class="text-muted-foreground text-[12px]">
		Last search: {lastSearch || "—"}
	</p>
</div>
```

### With FormField

```svelte
<script lang="ts">
	// FormField ships from an earlier wave and supplies controlId,
	// aria-describedby and aria-invalid to SearchInput through context —
	// SearchInput itself needs no id or invalid prop here.
	import { FormField } from "fancy-ui-svelte";
	import { SearchInput } from "fancy-ui-svelte";

	let query = $state("");
</script>

<FormField label="Find a component" description="Matches by name or tag." class="w-full max-w-sm">
	<SearchInput sound bind:value={query} />
</FormField>
```
