Input Search
Search field with a leading icon and a one-click clear button. Controlled or uncontrolled.
Examples
Controlled with clear
A leading icon, a one-click clear button, and a live query readout.
Type to search — the × clears the field
import { InputSearch } from "@codefast/ui/input-search";
import { useState } from "react";
export function InputSearchControlled() {
const [query, setQuery] = useState("");
return (
<div className="w-full max-w-xs space-y-3">
<InputSearch placeholder="Search components…" value={query} onChange={(value) => setQuery(value ?? "")} />
<p className="text-center text-xs text-ui-muted">
{query ? (
<>
Searching for <span className="font-medium text-ui-fg">“{query}”</span>
</>
) : (
"Type to search — the × clears the field"
)}
</p>
</div>
);
}
Live filtering
Drive a results list from the controlled value.
- Apple
- Apricot
- Banana
- Blueberry
- Cherry
- Grape
- Mango
- Orange
import { InputSearch } from "@codefast/ui/input-search";
import { useState } from "react";
const FRUITS = ["Apple", "Apricot", "Banana", "Blueberry", "Cherry", "Grape", "Mango", "Orange"];
export function InputSearchWithResults() {
const [query, setQuery] = useState("");
const matches = FRUITS.filter((fruit) => fruit.toLowerCase().includes(query.toLowerCase()));
return (
<div className="w-full max-w-xs space-y-2">
<InputSearch placeholder="Filter fruit…" value={query} onChange={(value) => setQuery(value ?? "")} />
<ul className="rounded-lg border border-ui-border">
{matches.length > 0 ? (
matches.map((fruit) => (
<li key={fruit} className="border-b border-ui-border/60 px-3 py-1.5 text-sm text-ui-fg last:border-0">
{fruit}
</li>
))
) : (
<li className="px-3 py-1.5 text-sm text-ui-muted">No matches</li>
)}
</ul>
</div>
);
}
Disabled
A non-interactive search field.
import { InputSearch } from "@codefast/ui/input-search";
export function InputSearchDisabled() {
return (
<div className="w-full max-w-xs">
<InputSearch disabled defaultValue="Indexing…" placeholder="Search" />
</div>
);
}
Usage
The minimal import and composition — see Examples below for styled, real-world variants.
import { InputSearch } from "@codefast/ui/input-search";
export function InputSearchUsage() {
return <InputSearch placeholder="Search..." />;
}
Anatomy
How the parts nest — every slot the component exposes, in composition order.
Features
- Works controlled or fully uncontrolled — useControllableState means defaultValue alone (no value/onChange) still manages state internally.
- The clear (×) button only renders once there’s a value, and clicking it calls onChange("") — the same path as clearing manually.
API reference
Props for each part of the component. All native element props are also forwarded.
InputSearch
A search field with a leading icon and a clear (×) button.
valuestringThe controlled query.
onChange(value?: string) => voidCalled when the query changes — also fires with an empty value on clear.
defaultValuestringInitial query when uncontrolled.
placeholderstringHint text shown when empty.
Accessibility
Built to be keyboard-navigable and screen-reader friendly out of the box.
| Key | Function |
|---|---|
| Esc | Clears the field in browsers that support it natively for type=search (e.g. WebKit) — not implemented in JS. |
- Renders type=search with an accessible clear button.
- Give it a label (visible or aria-label) describing what is searched.
- Debounce expensive queries; don’t fetch on every keystroke.
Guidelines
Conventions that keep usage consistent across an app.
Do
- Use for filtering lists and global search.
- Show the active query and a way to clear it.
Don’t
- Don’t use it for non-search text entry.
- Don’t hide the clear affordance once there’s a value.
Explore further
Ready to integrate?
Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.