Skip to main contentSkip to docs navigation

Search Field

A search input with a search icon and a clear button, which clears with Escape and reports Enter.

Overview

SearchField is a native <input type="search">, built on React Aria's useSearchField. It renders chassis-css's input adorn shape: a .form-input around the input, with a search icon at its start and, while there is a value, a button that clears it. Like the other form fields, it takes label, help and the validation props, and renders a FormLabel, FormHelp and FormFeedback wired to it.

Pages and posts.
JSX
import { SearchField } from '@chassis-ui/react'

export const Example = () => (
  <SearchField help="Pages and posts." label="Search" placeholder="Search…" />
)

The field takes chassis-css's sizes and states. Its own styles, which hide the browser's clear button and color the clear button by the field's state, are in @chassis-ui/react/style.css (see Theming).

Value

Pass value and onChange to control the value, or defaultValue to let the component manage it. The clear button and the Escape key empty the field, keep focus on the input and call onClear; Escape in an empty field does nothing, so it reaches a dialog or menu around the field. An onKeyDown given to the field stops every key there, as in React Aria, unless it calls event.continuePropagation().

  • Apple
  • Apricot
  • Banana
  • Blackberry
  • Cherry
  • Grape
  • Mango
  • Peach
JSX
import { useState } from 'react'
import { List, ListItem, SearchField, Stack } from '@chassis-ui/react'

const fruits = ['Apple', 'Apricot', 'Banana', 'Blackberry', 'Cherry', 'Grape', 'Mango', 'Peach']

export const Example = () => {
  const [query, setQuery] = useState('')
  const matches = fruits.filter((fruit) => fruit.toLowerCase().includes(query.toLowerCase()))
  return (
    <Stack direction="vertical" gap="md" style={{ maxWidth: '20rem' }}>
      <SearchField label="Filter fruits" onChange={setQuery} value={query} />
      <List>
        {matches.map((fruit) => (
          <ListItem key={fruit}>{fruit}</ListItem>
        ))}
        {matches.length === 0 && <ListItem>No fruit matches “{query}”.</ListItem>}
      </List>
    </Stack>
  )
}

Submitting

onSubmit receives the value when Enter is pressed, with or without a modifier key and in a read-only field too. With it, Enter no longer submits the input's form: use it for a search that runs in place.

Press Enter to search.
JSX
import { useState } from 'react'
import { SearchField } from '@chassis-ui/react'

export const Example = () => {
  const [searched, setSearched] = useState('')
  return (
    <SearchField
      help={searched ? `Searched for “${searched}”.` : 'Press Enter to search.'}
      label="Search"
      onClear={() => setSearched('')}
      onSubmit={setSearched}
    />
  )
}

In a form

Without onSubmit, Enter submits the form as in any text input, and with name the value is submitted under that name. Resetting the form restores defaultValue.

JSX
import { useState } from 'react'
import { Button, SearchField, Stack } from '@chassis-ui/react'

export const Example = () => {
  const [submitted, setSubmitted] = useState('')
  return (
    <form
      onSubmit={(event) => {
        event.preventDefault()
        setSubmitted(JSON.stringify(Object.fromEntries(new FormData(event.currentTarget))))
      }}
    >
      <Stack direction="vertical" gap="md" style={{ maxWidth: '20rem' }}>
        <SearchField
          help={submitted ? `Submitted: ${submitted}` : undefined}
          label="Search"
          name="q"
        />
        <Button type="submit">Search</Button>
      </Stack>
    </form>
  )
}

Icons

searchIcon and clearIcon replace the two icons, by name or as an element; searchIcon={false} leaves the search icon out. To replace them everywhere, map search and clear in IconProvider's icons.

JSX
import { SearchField } from '@chassis-ui/react'

export const Example = () => (
  <>
    <SearchField aria-label="Without the search icon" defaultValue="chassis" searchIcon={false} />
    <SearchField
      aria-label="Other icons"
      clearIcon="xmark-circle-solid"
      defaultValue="chassis"
      searchIcon="search-solid"
    />
  </>
)

Sizing

JSX
import { SearchField } from '@chassis-ui/react'

export const Example = () => (
  <>
    <SearchField aria-label="Small" defaultValue="chassis" size="sm" />
    <SearchField aria-label="Medium" defaultValue="chassis" />
    <SearchField aria-label="Large" defaultValue="chassis" size="lg" />
  </>
)

States

disabled and readOnly keep the value from changing, and leave the clear button out. invalid and valid color the field and show invalidFeedback or validFeedback.

Type at least two letters.
12 results.
JSX
import { SearchField } from '@chassis-ui/react'

export const Example = () => (
  <>
    <SearchField defaultValue="chassis" disabled label="Disabled" />
    <SearchField defaultValue="chassis" label="Read only" readOnly />
    <SearchField
      defaultValue="c"
      invalid
      invalidFeedback="Type at least two letters."
      label="Invalid"
    />
    <SearchField defaultValue="chassis" label="Valid" valid validFeedback="12 results." />
  </>
)

Accessibility

The input has the searchbox role and is named by its label; give it an aria-label when there is no visible one. The search icon is hidden from assistive technology. The clear button is named "Clear search" in the locale's language, or by clearAriaLabel, and is left out of the tab order, since Escape does the same from the input.

API

SearchField

PropTypeDefault
className?string—
A string of all className you want applied to the component.
clearAriaLabel?string—
The accessible name of the clear button. Defaults to react-aria's, in the locale's language ("Clear search").
clearIcon?IconValue—
The icon of the clear button, in place of IconProvider's clear icon.
defaultValue?string—
The value of the field, uncontrolled.
disabled?boolean—
Toggle the disabled state for the component.
help?ReactNode—
A description for the field, rendered below it.
invalid?boolean—
Set component validation state to invalid. Left unset, the component shows as invalid by itself from a submit it held back (an empty required one, say) until it no longer would, or its form is reset. Set it, to false too, and it alone decides.
invalidFeedback?ReactNode—
An error message for the field, rendered below it when invalid is set, and after a submit the field held back, until it no longer would.
label?ReactNode—
The field's caption, rendered as a FormLabel associated with the input.
onChange?((value: string) => void)—
Handler that is called when the value changes.
onClear?(() => void)—
Handler that is called when the field is cleared, by the clear button or the Escape key.
onSubmit?((value: string) => void)—
Handler that is called when the Enter key is pressed, with the value. When set, Enter, with or without a modifier key, no longer submits the input's form.
readOnly?boolean—
Toggle the readonly state for the component.
searchIcon?false | IconValue—
The icon at the field's start, in place of IconProvider's search icon. false leaves it out.
size?"sm" | "lg"—
Size the component sm or lg.
valid?boolean—
Set component validation state to valid.
validFeedback?ReactNode—
A success message for the field, rendered below it when valid is set.
value?string—
The value of the field, controlled.