SearchField

Search input with a built-in dense clear button.

moleculesSource
import { SearchField } from '@elirobinson/react/components/molecules/SearchField';

Styles: @elirobinson/react/styles/molecules/SearchField.css — already included when you import @elirobinson/react/styles.css.

Show code
import { SearchField } from '@elirobinson/react/components/molecules/SearchField';

export default function Basic() {
  return <SearchField aria-label="Search coaching guides" defaultValue="practice plan" />;
}

When to use it

Use SearchField for an inline filter — a table toolbar, a list you want to narrow as you type. It's an <input type="search"> with a built-in clear button, nothing more. For filtering that also opens a list of matching options to pick from, use Combobox instead; SearchField never renders a popup.

Like Input, it supports both patterns: pass value/onValueChange to drive it from your own state (as in the filtering example below), or defaultValue and let it manage its own state uncontrolled.

Controlled, filtering a list

  • Weeknight Pasta
  • Sheet-Pan Chicken
  • Ten-Minute Tacos
  • Fridge Soup
Show code
import { useState } from 'react';

import { SearchField } from '@elirobinson/react/components/molecules/SearchField';

const RECIPES = ['Weeknight Pasta', 'Sheet-Pan Chicken', 'Ten-Minute Tacos', 'Fridge Soup'];

export default function Controlled() {
  const [query, setQuery] = useState('');
  const results = RECIPES.filter((recipe) => recipe.toLowerCase().includes(query.toLowerCase()));

  return (
    <div className="demo-col">
      <SearchField aria-label="Search recipes" value={query} onValueChange={setQuery} />
      <ul>
        {results.length > 0 ? (
          results.map((recipe) => <li key={recipe}>{recipe}</li>)
        ) : (
          <li>No recipes match &quot;{query}&quot;.</li>
        )}
      </ul>
    </div>
  );
}

Props

PropTypeDefaultDescription
defaultValuestring
onValueChange((value: string) => void)
valuestring

Also accepts all Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'onChange' | 'value' | 'defaultValue'> props.

Accessibility

  • SearchField doesn't render its own <label> — unlike Input, it has no label prop. Give it an accessible name yourself with aria-label (as every demo on this page does) or aria-labelledby, or wrap it in FormField if you want a visible label plus hint/error text.
  • The underlying element is a native <input type="search">, which browsers and screen readers expose with the searchbox role automatically.
  • The clear button only renders once there's a value, carries aria-label="Clear search", and returns focus to the input after clearing — so clearing never strands keyboard focus.
  • The clear button follows dense inline sizing (MUI/shadcn scale), not the 44px primary-control minimum.
  • Keyboard: standard text-input behavior — type to filter, Tab reaches the clear button when it's present, Enter/Space activates it. There's no Escape-to-clear shortcut wired in.
  • The ref forwards to the real <input> element, not the wrapping <div> — calling ref.current.focus() works directly. Combobox and CommandPalette both depend on this.

Do

  • Always give it an aria-label or aria-labelledby — it has no visible label of its own.
  • Debounce the filtering logic in onValueChange for anything more expensive than an in-memory array filter.
  • Show a "no results" message in the list below rather than leaving it silently empty.
  • Use defaultValue for a simple, self-contained search box that doesn't need to sync with other state.

Don't

  • Wrap Input inside FormField expecting SearchField-like behavior — they're different components with different label handling.
  • Expect a popup of matching options — that's Combobox, not SearchField.
  • Rely on Escape to clear the field; wire your own keydown handler if you want that.
  • Forget the clear button's dense hit area when laying out tight toolbar spacing — verify it doesn't overlap adjacent controls.