Dots

Search Field

A labeled search box with a clear button, Escape to clear and Enter to submit.

  • Maya Chen
  • Leo Park
  • Ana Ruiz
  • Sam Okafor
  • Ivy Novak
"use client"

import * as React from "react"

Use a search field to filter a list or start a search, such as finding a contact to pay or a past payment. It is a pill with a search icon and a clear button that shows while there is text. Pick TokenSearchField when people filter with operators like amount or date, and a TextField when the value is data to save rather than a query.

Installation

pnpm dlx shadcn@latest add @dots/search-field

Usage

import {
  SearchField,
  SearchFieldDescription,
  SearchFieldError,
  SearchFieldInput,
  SearchFieldLabel,
} from "@/components/ui/search-field"
<SearchField aria-label="Search payments">
  <SearchFieldInput placeholder="Search payments" />
</SearchField>

Composition

Use the following composition to build a search field:

SearchField
├── SearchFieldLabel
├── SearchFieldInput
│   └── addons (children)
├── SearchFieldDescription
└── SearchFieldError

Features

  • Built on React Aria SearchField: Escape clears the text, Enter calls onSubmit
  • A clear button that renders only while there is text and the field is editable
  • Optional label, description and error, wired to the input for screen readers
  • Extra addons, such as a keyboard hint, through SearchFieldInput children
  • Controlled or uncontrolled with value or defaultValue

Label and description

Add a SearchFieldLabel when the screen does not already say what is searched, and a SearchFieldDescription for a short hint.

Search people you have paid before.
import {
  SearchField,
  SearchFieldDescription,

Submit

Use onSubmit to run a search when the user presses Enter, and onClear to reset it.

Press Enter to search

"use client"

import * as React from "react"

Keyboard hint

Pass an InputGroupAddon as children of SearchFieldInput to show a shortcut or a filter button at the end of the pill. The hint is only a label: bind the key yourself and focus the input through a ref. In this demo, pressing / focuses the field.

/
"use client"

import * as React from "react"
const inputRef = React.useRef<HTMLInputElement>(null)
 
React.useEffect(() => {
  function onKeyDown(event: KeyboardEvent) {
    const target = event.target as HTMLElement | null
    if (event.key !== "/" || target?.closest("input, textarea")) return
    event.preventDefault()
    inputRef.current?.focus()
  }
  document.addEventListener("keydown", onKeyDown)
  return () => document.removeEventListener("keydown", onKeyDown)
}, [])
 
<SearchFieldInput ref={inputRef}>
  <InputGroupAddon align="inline-end">
    <Kbd>/</Kbd>
  </InputGroupAddon>
</SearchFieldInput>

Invalid

Set isInvalid and add a SearchFieldError that says what to fix.

Type at least 3 characters.
import {
  SearchField,
  SearchFieldError,

Disabled

Set isDisabled to turn the field off. The clear button is hidden and the label and description are dimmed.

Search is off while you are offline.
import {
  SearchField,
  SearchFieldDescription,

Accessibility

React Aria gives the input type="search", connects the label, description and error to it, clears on Escape and submits on Enter. The clear button has the localized name "Clear search" by default.

Name the field

Give every search field a name. Use a SearchFieldLabel, or aria-label when a visible label would repeat the screen title.

<SearchField aria-label="Search contacts">
  <SearchFieldInput placeholder="Search contacts" />
</SearchField>

Extra buttons

React Aria hands every button inside SearchField the clear action. Buttons passed as SearchFieldInput children are shielded from it. Any other button placed directly under SearchField needs slot={null}, and an icon-only button needs an aria-label.

<SearchField aria-label="Search payments">
  <SearchFieldInput />
  <Button slot={null} variant="ghost" aria-label="Filters">
    <FilterIcon />
  </Button>
</SearchField>

Placeholder is not a label

The placeholder disappears as soon as the user types. Do not use it as the only name of the field.

API Reference

SearchField

The root. Renders a React Aria SearchField and accepts all of its props.

PropTypeDefaultDescription
valuestring-The text, when controlled.
defaultValuestring-The starting text, when uncontrolled.
onChange(value: string) => void-Called when the text changes.
onSubmit(value: string) => void-Called when the user presses Enter.
onClear() => void-Called when the field is cleared.
isDisabledboolean-Turns the field off.
isReadOnlyboolean-The text can be selected but not changed.
isInvalidboolean-Shows the field as invalid and reveals the error.
validationBehavior"native" | "aria""native"Whether validation blocks form submission.
classNamestring-Additional classes to apply to the root.
...propsSearchFieldProps-Other React Aria SearchField props.

SearchFieldLabel

The visible name of the field. Renders a Label and accepts all of its props.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the label.

SearchFieldInput

The pill with the search icon, the input and the clear button. Accepts all InputGroupInput props, which go to the input.

PropTypeDefaultDescription
placeholderstring"Search"Hint text shown while the field is empty.
clearLabelstring-Accessible name of the clear button. "Clear search" by default.
childrenReact.ReactNode-Extra addons after the input. Buttons here do not clear the field.
classNamestring-Additional classes to apply to the pill around the input.

SearchFieldDescription

Help under the field, read with it. Renders a React Aria Text with slot="description".

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the description.

SearchFieldError

Says what to fix. Shown while the field is invalid. Renders a React Aria FieldError.

PropTypeDefaultDescription
classNamestring-Additional classes to apply to the error.