SearchBar
MD3 search bar: a 56dp docked field that, given results as children,
opens a docked search view with suggestions under the bar.
Basic
Without children the component is a plain search field. onSearch fires
on Enter (not while an IME composition is being committed).
<SearchBar aria-label="Search" placeholder="Search messages" onSearch={(query) => runSearch(query)} />
Search view with results
Pass results as children — typically a List of clickable ListItems.
The view opens on focus (or ArrowDown) and closes on Escape, an outside
click, or a scrim click. It renders in the top layer, pinned under the bar,
so ancestors' overflow can't clip it.
const [query, setQuery] = useState('')
const [open, setOpen] = useState(false)
<SearchBar
aria-label="Search fruit"
value={query}
onChange={(event, next) => setQuery(next)}
open={open}
onOpenChange={setOpen}
>
<List>
{matches.map((fruit) => (
<ListItem key={fruit} headline={fruit} onClick={() => { setQuery(fruit); setOpen(false) }} />
))}
</List>
</SearchBar>
The open state follows the usual pattern: open + onOpenChange for
controlled, defaultOpen for uncontrolled. Likewise the query: value +
onChange(event, value), or defaultValue.
Leading and trailing slots
startIcon replaces the default search glyph and endIcon adds a trailing
control. Both are 48dp slots, so an IconButton (back, menu, voice search,
clear…) fits and stays in the tab order. Mark a purely decorative custom
icon aria-hidden yourself.
<SearchBar
aria-label="Search"
startIcon={<IconButton variant="standard" aria-label="Back" icon={<ArrowBack />} />}
endIcon={<IconButton variant="standard" aria-label="Voice search" icon={<Mic />} />}
/>
Disabled
The native input
ref and extra props land on the root (role="search" landmark). Use
inputRef for the native <input>, and inputProps for attributes without
a dedicated prop — the component's own wiring wins on conflicts.
<SearchBar inputRef={inputRef} inputProps={{ 'aria-label': 'Search docs', maxLength: 80 }} />
Accessibility
- The root is a
role="search"landmark. With results the input becomes an APGcombobox(aria-expanded,aria-controls,aria-autocomplete="list"); otherwise it is a native search input. - Keyboard: ArrowDown opens the view and moves into the results;
ArrowUp / ArrowDown / Home / End move between result items (ArrowUp from
the first returns to the input); Escape closes the view and returns focus
to the input; Enter calls
onSearch. Moving focus out closes the view. - Opening the view is announced through a polite live region with
suggestionsLabel(default'Suggestions below') — localize it with the rest of your strings. - A consumer
onKeyDownthat callspreventDefault()opts out of the built-in handling for that key.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Suggestion / result content shown in the open search view. |
defaultOpen | boolean | false | Uncontrolled initial open state. |
defaultValue | string | — | Uncontrolled initial value. |
disabled | boolean | false | Disables the input and applies disabled styling. |
endIcon | ReactNode | — | Trailing icon / control (48dp slot, e.g. a mic or clear `IconButton`). |
inputProps | InputHTMLAttributes<HTMLInputElement> | — | Extra attributes spread on the native `<input>` — the escape hatch for attributes without a dedicated prop (e.g. a distinct `aria-label` for the searchbox, `maxLength`, `pattern`, extra `aria-*`, …). Precedence: the component's own wiring always wins over conflicting `inputProps` keys — the controlled `value` / `onChange` and internal `onFocus` / `onKeyDown` handling, `type="search"`, `className`, and every dedicated input prop the component sets (`placeholder`, `name`, `disabled`, …). |
inputRef | Ref<HTMLInputElement> | — | Ref to the native `<input>` element (the forwarded `ref` points at the root). |
name | string | — | Native input `name`. |
onBlur | FocusEventHandler<HTMLInputElement> | — | Blur handler for the native input. |
onChange | ((event: ChangeEvent<HTMLInputElement, Element>, value: string) => void) | — | Fires with the native event and the new query. |
onFocus | FocusEventHandler<HTMLInputElement> | — | Focus handler for the native input. |
onKeyDown | KeyboardEventHandler<HTMLInputElement> | — | Key handler for the native input. |
onOpenChange | ((open: boolean) => void) | — | Notified when the open state should change. |
onSearch | ((value: string) => void) | — | Fires when the user submits (Enter). |
open | boolean | — | Controlled open (search-view) state. |
placeholder | string | Search | Native input `placeholder`. |
startIcon | ReactNode | — | Leading content (defaults to a decorative search glyph). Rendered in a 48dp slot, so it may be a navigation `IconButton` (back / menu) — it stays in the tab order and the accessibility tree. A purely decorative custom icon should carry `aria-hidden` itself. |
suggestionsLabel | string | Suggestions below | Text announced (polite live region) when the search view opens — the equivalent of Compose's "Suggestions below" state description. Localize it with the rest of your UI strings. |
value | string | — | Controlled query value. |