Skip to main content

Search Input

SearchInput is an Input with type="search": a leading icon, a clear button while the field has a value, a spinner while loading, an onSearch callback on Enter and Escape-to-clear. Every Input prop applies except type and leadingContent. A root <div> wraps the Input, so className and fullWidth go on that root and inputClassName onto the native input.

import SearchInput from '@zuilib/components/search-input'

Basic

Type to reveal the clear button; Escape or the button empties the field through a native input event, so onChange fires with '' and a controlled value follows without extra wiring.

Loading example

Loading

loading adds a trailing spinner, sets aria-busy and announces loadingLabel from a visually hidden role="status" region. Enter is ignored while loading.

Loading example

Sizes and icon

sm, md and lg come from the Input's control tokens; the icons step with them. icon={false} removes the leading glyph; any node replaces it.

Loading example

In a FormItem

The inner Input receives the item's id, aria-labelledby and aria-describedby; disabled is inherited when left unset (and hides the clear button).

Loading example

Props

SearchInputProps extends InputProps (variant, state, invalid, unsaved, fullWidth, inputClassName, trailingContent, native input attributes) minus type and leadingContent, with:

PropTypeDefaultDescription
onSearch(value: string) => voidPlain Enter (no modifier, not during IME composition, not while loading). When set, Enter is consumed and does not submit an enclosing <form>
onClear() => voidAfter the clear button or Escape empties the field; onChange fires with '' first
loadingbooleanfalseTrailing spinner, aria-busy, and loadingLabel in a visually hidden role="status" region
loadingLabelstring'Loading'Announced while loading
clearLabelstring'Clear'Accessible name of the clear button
iconReactNode | falsemagnifying glassLeading glyph, aria-hidden; false renders none
trailingContentReactNodeAfter the spinner and the clear button
size'sm' | 'md' | 'lg''md'
fullWidthbooleanfalsew-full on the root
classNamestringMerged onto the root <div>
inputClassNamestringMerged onto the native <input>

The ref points at the native <input>. An imperative ref.current.value = … fires no event; dispatch an input event after it (or control the field) for the clear button to notice.

Slots

SlotElementNotes
[data-slot="search-input"]divRoot. Also data-size, data-loading, data-has-value
[data-slot="search-input-control"]divThe Input frame (its data-slot="input" is replaced). Same data attributes as the root
[data-slot="input-control"]inputThe native type="search" input, browser cancel glyphs removed
[data-slot="search-input-icon"]spanLeading glyph; aria-hidden
[data-slot="search-input-spinner"]svgPresent while loading
[data-slot="search-input-status"]spanVisually hidden role="status" with loadingLabel
[data-slot="search-input-clear"]buttonPresent while there is a value and the field is enabled and editable
[data-slot="search-input-clear-icon"]svg
/* Consumer CSS: keep the clear button visible on hover only */
[data-slot="search-input"]:not(:hover):not(:focus-within) [data-slot="search-input-clear"] {
opacity: 0;
}

Tokens

TokenUsed for
--control-height-*, --control-padding-x-*Field box per size (from Input)
--input, --background, --ringFrame border, fill and focus ring
--muted-foreground, --foregroundIcon, spinner and clear button colours
--radius-smClear button corners
--duration-fastClear button colour transition

Accessibility

  • A real <input type="search" enterKeyHint="search">; inside a FormItem it gets the field's id, aria-labelledby and aria-describedby. Outside one, pass aria-label or a Label htmlFor.
  • The leading icon is decorative (aria-hidden); the clear button is a real button named by clearLabel, and it keeps focus in the input when clicked.
  • Keyboard: Enter calls onSearch (plain Enter only; modifier keys and IME composition are left alone); Escape clears a non-empty field and calls onClear. An empty field leaves Escape alone so an enclosing dialog can close.
  • loading sets aria-busy on the input and announces loadingLabel from a polite role="status" region.
  • The clear button hides itself when the field is disabled (also through a Headless UI Fieldset disabled) or read-only.
  • Input: the field this builds on, with the shared variants and states.
  • Combobox: search with suggestions.
  • NumberInput: another Input with built-in behaviour.
  • FormItem: the field wrapper that wires label, description and message.