Make an accessible search bar in React
Tags: react, a11y, html, beginners
Published: 2021-05-03 06:11:47

# Make an accessible search bar in React

This post is going to explain how to take a basic search input and make it accessible to everyone. 

## Let's begin

I have began by creating a simple app. It contains a header with a search input and navigation links. In the main of the app all the shopping items are displayed. You can search for items in the search input and items that contain the character you searched for are displayed. 

```jsx
import { useState } from 'react';
import styles from '../styles/Home.module.css';

export default function Home() {
  const [term, setTerm] = useState('');
  const [searchedResult, setSearchedResult] = useState('');

  const items = [
    { name: 'apple' },
    { name: 'banana' },
    { name: 'pear' },
    { name: 'apple tango' },
    { name: 'mango' },
    { name: 'banana milkshake' },
  ];

  const handleSubmit = e => {
    e.preventDefault();
    setSearchedResult(term);
    setTerm('');
  };

  const handleChange = e => {
    setTerm(e.target.value);
  };

  const filteredItems = items.filter(item =>
    item.name.toLocaleLowerCase().includes(searchedResult.toLocaleLowerCase())
  );

  return (
    <div className={styles.container}>
      <header className={styles.header}>
        <div>
          <a href='/'>LOGO</a>
        </div>
        <form className={styles.search} onSubmit={handleSubmit}>
          <input
            type='text'
            id='search'
            name='search'
            onChange={handleChange}
            value={term || ''}
            placeholder='search'
          />
          <button type='submit' onClick={handleSubmit}>
            Search
          </button>
        </form>
        <nav className={styles.nav}>
          <div>
            <a href='/'>About</a>
          </div>
          <div>
            <a href='/'>Contact</a>
          </div>
        </nav>
      </header>
      <main className={styles.main}>
        <h3>Items</h3>
        {filteredItems && (
          <ul>
            {filteredItems.map(item => (
              <li key={item.name}>{item.name}</li>
            ))}
          </ul>
        )}
      </main>
    </div>
  );
}

```

This app works but it's not accessible. Which means it is no good. This app should be accessible to everyone and anyone.

## Making the search functionality accessible

First we can add a role of search to the form. This will help assistive technology to inform the user that there is a search option. Screen readers will be given the option to navigate to this section.

### Search role

` <form role='search' className={styles.search} onSubmit={handleSubmit}>`

### Type search

Modern browsers support an input type of "search". This works just like `type="text"` but with added benefits. It helps with autocomplete options which is useful for users with dyslexia to avoid spelling mistakes when they need to use this option.

Changes to make:
```jsx
  <input
        type='search'
        id='search'
        name='search'
        onChange={handleChange}
        value={term || ''}
        placeholder='search'
          />
``` 

### Labelling 
For user controlled elements like the search `input` we have, needs a `<label>`. Screen readers will read this label out to the user.

```jsx
   <label htmlFor='search'>Search:</label>
          <input
            type='search'
            id='search'
            name='search'
            onChange={handleChange}
            value={term || ''}
            placeholder='search'
          />
```

The above code will display the label in the browser and be visible. Sometimes you might not want the label to be visible though. You must include a label but you can hide it using CSS but you **must** include the `htmlFor` attribute.


```jsx
<label htmlFor='search' className='visualy-hidden'>Search:</label>
  <input
    type='search'
    id='search'
    name='search'
    onChange={handleChange}
    value={term || ''}
    placeholder='search'
      />
```

You can add an `aria-label` direct to the `input` if you do not wish to use a `label` element. This will be read out by screen readers.


```jsx
 <label htmlFor='search'>Search:</label>
          <input
            aria-label='Enter your search term'
            type='search'
            id='search'
            name='search'
            onChange={handleChange}
            value={term || ''}
            placeholder='search'
          />
```

An additional step would be to app a `title` to the input.

```jsx
 <label htmlFor='search'>Search:</label>
          <input
            title='search for food'
            aria-label='Enter your search term'
            type='search'
            id='search'
            name='search'
            onChange={handleChange}
            value={term || ''}
            placeholder='search'
          />
```

If you're interested diving deeper this article [here](https://www.w3.org/WAI/tutorials/forms/labels/) goes deeper.

### Validation 

Our form needs validation to alert the user if there is any issues.

First we can add a required field to make help make sure the search functions has been typed in.

```jsx
 <label htmlFor='search'>Search:</label>
          <input
            title='search for food'
            aria-label='Enter your search term'
            type='search'
            id='search'
            name='search'
            onChange={handleChange}
            value={term || ''}
            placeholder='search'
            required
          />
```

We can also add error handling of our own. You can hide this with CSS. 

I'm going to use error state and display a span containing the error message.

```jsx
const [error, setError] = useState(null);
```

```jsx
const handleSubmit = e => {
    e.preventDefault();
    setSearchedResult(term);
    setTerm('');
    if (filteredItems.length === 0) {
      setError('No items matching your search please be more specific');
    }
  };
```

```jsx
// className can be used to hide this
  <span className='errMsg' role='status'>
  {error}
  </span>
```

Heres the code so far:

```jsx
export default function Home() {
  const [term, setTerm] = useState('');
  const [searchedResult, setSearchedResult] = useState('');
  const [error, setError] = useState(null);

  const items = [
    { name: 'apple' },
    { name: 'banana' },
    { name: 'pear' },
    { name: 'apple tango' },
    { name: 'mango' },
    { name: 'banana milkshake' },
  ];

  const handleSubmit = e => {
    e.preventDefault();
    setSearchedResult(term);
    setTerm('');
    if (filteredItems.length === 0) {
      setError('No items matching your search please be more specific');
    }
  };

  const handleChange = e => {
    setTerm(e.target.value);
  };

  const filteredItems = items.filter(item =>
    item.name.toLocaleLowerCase().includes(searchedResult.toLocaleLowerCase())
  );

  return (
    <div className={styles.container}>
      <header className={styles.header}>
        <div>
          <a href='/'>LOGO</a>
        </div>
        <form role='search' className={styles.search} onSubmit={handleSubmit}>
          <label htmlFor='search'>Search:</label>
          <input
            title='search for food'
            aria-label='Enter your search term'
            type='search'
            id='search'
            name='search'
            onChange={handleChange}
            value={term || ''}
            placeholder='search'
            required
          />

          <button type='submit' onClick={handleSubmit}>
            Search
          </button>
        </form>
        <nav className={styles.nav}>
          <div>
            <a href='/'>About</a>
          </div>
          <div>
            <a href='/'>Contact</a>
          </div>
        </nav>
      </header>
      <main className={styles.main}>
        <h3>Items</h3>
        <span className='errMsg' role='status'>
          {error}
        </span>
        {filteredItems && (
          <ul>
            {filteredItems.map(item => (
              <li key={item.name}>{item.name}</li>
            ))}
          </ul>
        )}
      </main>
    </div>
  );
}
```

Our forms looking good. There is more we can do regarding error messages etc but this is just to give a good idea and to start thinking about it.

### Useful links to read more.

- [Aria search role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Search_role)
- [Input search](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/search)
- [Label controls](https://www.w3.org/WAI/tutorials/forms/labels/)
- [Form validation](https://webaim.org/techniques/formvalidation/)

## Summary 

To summarise you need to realise that there is users that have different requirements to yourself. You need to ensure that they are aware of all useful content within your web page.

## 🚨 Attention!  🚨

If you know something that could be added to this article please comment. Improving site accessibility as a web dev community is for the greater good.