useSearchField

Provides the behavior and accessibility implementation for a search field.

installyarn add @react-aria/searchfield
version3.2.0
usageimport {useSearchField} from '@react-aria/searchfield'

API#


useSearchField( props: AriaSearchFieldProps, state: SearchFieldState, inputRef: RefObject<HTMLInputElementHTMLTextAreaElement> ): SearchFieldAria

Features#


Search fields can be built with <input type="search">, but these can be hard to style consistently cross browser. useSearchField helps achieve accessible search fields that can be styled as needed.

  • Built with a native <input type="search"> element
  • Visual and ARIA labeling support
  • Keyboard submit handling via the Enter key
  • Keyboard support for clearing the search field with the Escape key
  • Custom clear button support with internationalized label for accessibility

Anatomy#


Search field anatomy diagramValueLabelInputLabelClear button

Search fields consist of an input element, a label, and an optional clear button. useSearchField automatically manages the labeling and relationships between the elements, and handles keyboard events. Users can press the Escape key to clear the search field, or the Enter key to trigger the onSubmit event.

useSearchField returns two sets of props that you should spread onto the appropriate element:

NameTypeDescription
labelPropsLabelHTMLAttributes<HTMLLabelElement>Props for the text field's visible label element (if any).
inputPropsInputHTMLAttributes<HTMLInputElementHTMLTextAreaElement>Props for the input element.
clearButtonPropsAriaButtonPropsProps for the clear button.
descriptionPropsHTMLAttributes<HTMLElement>Props for the searchfield's description element, if any.
errorMessagePropsHTMLAttributes<HTMLElement>Props for the searchfield's error message element, if any.

State is managed by the useSearchFieldState hook in @react-stately/searchfield. The state object should be passed as an option to useSearchField.

If there is no visual label, an aria-label or aria-labelledby prop must be passed instead to identify the element to screen readers.

Example#


import {useSearchFieldState} from '@react-stately/searchfield';

function SearchField(props) {
  let {label} = props;
  let state = useSearchFieldState(props);
  let ref = React.useRef();
  let {labelProps, inputProps} = useSearchField(props, state, ref);

  return (
    <div style={{display: 'flex', flexDirection: 'column', width: 200}}>
      <label {...labelProps}>{label}</label>
      <input {...inputProps} ref={ref} />
    </div>
  );
}

<SearchField
  label="Search"
  placeholder="What are you looking for?"
  onSubmit={(text) => alert(text)}
/>
import {useSearchFieldState} from '@react-stately/searchfield';

function SearchField(props) {
  let {label} = props;
  let state = useSearchFieldState(props);
  let ref = React.useRef();
  let {labelProps, inputProps} = useSearchField(
    props,
    state,
    ref
  );

  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'column',
        width: 200
      }}>
      <label {...labelProps}>{label}</label>
      <input {...inputProps} ref={ref} />
    </div>
  );
}

<SearchField
  label="Search"
  placeholder="What are you looking for?"
  onSubmit={(text) => alert(text)}
/>
import {useSearchFieldState} from '@react-stately/searchfield';

function SearchField(
  props
) {
  let {label} = props;
  let state = useSearchFieldState(
    props
  );
  let ref = React.useRef();
  let {
    labelProps,
    inputProps
  } = useSearchField(
    props,
    state,
    ref
  );

  return (
    <div
      style={{
        display: 'flex',
        flexDirection:
          'column',
        width: 200
      }}>
      <label
        {...labelProps}>
        {label}
      </label>
      <input
        {...inputProps}
        ref={ref}
      />
    </div>
  );
}

<SearchField
  label="Search"
  placeholder="What are you looking for?"
  onSubmit={(text) =>
    alert(text)
  }
/>

Styling#


This example uses CSS to reset the default browser styling for a search field and implement custom styles. It also includes a custom clear button, using props from useSearchField and useButton (from @react-aria/button).

import {useButton} from '@react-aria/button';

function SearchField(props) {
  let {label} = props;
  let state = useSearchFieldState(props);
  let ref = React.useRef();
  let {labelProps, inputProps, clearButtonProps} = useSearchField(
    props,
    state,
    ref
  );
  let {buttonProps} = useButton(clearButtonProps);

  return (
    <div className="search-field">
      <label {...labelProps}>{label}</label>
      <div>
        <input {...inputProps} ref={ref} />
        {state.value !== '' && <button {...buttonProps}></button>}
      </div>
    </div>
  );
}

<SearchField
  label="Search"
  placeholder="What are you looking for?"
  onSubmit={(text) => alert(text)}
/>
import {useButton} from '@react-aria/button';

function SearchField(props) {
  let {label} = props;
  let state = useSearchFieldState(props);
  let ref = React.useRef();
  let {
    labelProps,
    inputProps,
    clearButtonProps
  } = useSearchField(props, state, ref);
  let {buttonProps} = useButton(clearButtonProps);

  return (
    <div className="search-field">
      <label {...labelProps}>{label}</label>
      <div>
        <input {...inputProps} ref={ref} />
        {state.value !== '' && (
          <button {...buttonProps}></button>
        )}
      </div>
    </div>
  );
}

<SearchField
  label="Search"
  placeholder="What are you looking for?"
  onSubmit={(text) => alert(text)}
/>
import {useButton} from '@react-aria/button';

function SearchField(
  props
) {
  let {label} = props;
  let state = useSearchFieldState(
    props
  );
  let ref = React.useRef();
  let {
    labelProps,
    inputProps,
    clearButtonProps
  } = useSearchField(
    props,
    state,
    ref
  );
  let {
    buttonProps
  } = useButton(
    clearButtonProps
  );

  return (
    <div className="search-field">
      <label
        {...labelProps}>
        {label}
      </label>
      <div>
        <input
          {...inputProps}
          ref={ref}
        />
        {state.value !==
          '' && (
          <button
            {...buttonProps}></button>
        )}
      </div>
    </div>
  );
}

<SearchField
  label="Search"
  placeholder="What are you looking for?"
  onSubmit={(text) =>
    alert(text)
  }
/>
/* css */
.search-field {
  display: flex;
  flex-direction: column;
}

.search-field div {
  background: slategray;
  padding: 4px 0 4px 4px;
  border-radius: 4px;
  width: 250px;
  display: flex;
}

.search-field input {
  flex: 1;
  color: white;
  font-size: 15px;
}

.search-field input::-webkit-input-placeholder {
  color: rgba(255, 255, 255, 0.6);
}

.search-field input, .search-field button {
  -webkit-appearance: none;
  border: none;
  outline: none;
  background: none;
}

.search-field input::-webkit-search-cancel-button,
.search-field input::-webkit-search-decoration {
  -webkit-appearance: none;
}
/* css */
.search-field {
  display: flex;
  flex-direction: column;
}

.search-field div {
  background: slategray;
  padding: 4px 0 4px 4px;
  border-radius: 4px;
  width: 250px;
  display: flex;
}

.search-field input {
  flex: 1;
  color: white;
  font-size: 15px;
}

.search-field input::-webkit-input-placeholder {
  color: rgba(255, 255, 255, 0.6);
}

.search-field input, .search-field button {
  -webkit-appearance: none;
  border: none;
  outline: none;
  background: none;
}

.search-field input::-webkit-search-cancel-button,
.search-field input::-webkit-search-decoration {
  -webkit-appearance: none;
}
/* css */
.search-field {
  display: flex;
  flex-direction: column;
}

.search-field div {
  background: slategray;
  padding: 4px 0 4px 4px;
  border-radius: 4px;
  width: 250px;
  display: flex;
}

.search-field input {
  flex: 1;
  color: white;
  font-size: 15px;
}

.search-field
  input::-webkit-input-placeholder {
  color: rgba(
    255,
    255,
    255,
    0.6
  );
}

.search-field input,
.search-field button {
  -webkit-appearance: none;
  border: none;
  outline: none;
  background: none;
}

.search-field
  input::-webkit-search-cancel-button,
.search-field
  input::-webkit-search-decoration {
  -webkit-appearance: none;
}

Internationalization#


RTL#

In right-to-left languages, search fields should be mirrored. The label should be right aligned, along with the text in the input. Ensure that your CSS accounts for this.