Build a custom React autocomplete search component
Tags: react, javascript, webdev, tutorial
Published: 2022-09-06 06:47:37

In this tutorial we’ll be building a React autocomplete search component that provides suggestions as a user types a search query. There are a number of libraries that provide autocomplete functionality in React but we’ll be creating a custom component from scratch.

Let’s get started by setting up a basic app using [Create React App](https://w3collective.com/create-react-app/):
```
npx create-react-app react-autocomplete-search
```
Next create a new `data.js` file in the `/src` folder. This file contains an array that will be used to provide the autocomplete suggestions. In the real world you might want to replace this file with an API call to provide the data:

```javascript
export const autoCompleteData = [
    "Asparagus",
    "Beetroot",
    "Broccoli",
    "Cabbage", 
    "Carrot", 
    "Cauliflower", 
    "Celery", 
    "Corn", 
    "Eggplant", 
    "Lettuce", 
    "Mushroom", 
    "Onion", 
    "Parsnip", 
    "Pea", 
    "Potato", 
    "Pumpkin", 
    "Radish", 
    "Spinach",    
    "Tomato", 
    "Turnip", 
  ];
```

Then create a new `AutoComplete.js` file in the `/src` folder with the following structure:

```javascript
import { useState } from "react";

const AutoComplete = ({ data }) => {
  return (
    <div className="autocomplete">
      <input type="text" />     
    </div>
  );
};

export default AutoComplete;
```

We can now start building the component starting with the `State` variables:

```javascript
const [suggestions, setSuggestions] = useState([]);
const [suggestionIndex, setSuggestionIndex] = useState(0);
const [suggestionsActive, setSuggestionsActive] = useState(false);
const [value, setValue] = useState("");
```

- `suggestions` – array of suggestions to used populate the autocomplete menu.
- `suggestionIndex` – index of the active suggestion used for keyboard navigation.
- `suggestionsActive` – used to toggle the visibility of the autocomplete suggestions.
- `value` – autocomplete suggestion that the user has selected.

The autocomplete suggestions need to be triggered while the user is typing a query. For this we’ll use an `onChange` event that monitors for changes to the input field. We then filter the `autoCompleteData` to find the relevant suggestions:

```javascript
const handleChange = (e) => {
  const query = e.target.value.toLowerCase();
  setValue(query);
  if (query.length > 1) {
    const filterSuggestions = data.filter(
      (suggestion) => suggestion.toLowerCase().indexOf(query) > -1
    );
    setSuggestions(filterSuggestions);
    setSuggestionsActive(true);
  } else {
    setSuggestionsActive(false);
  }
};
```

Users will also need to be able to click an autocomplete suggestion and have that suggestion populate the input field. For this we’ll need to add the following function that is triggered by an `onClick` event:

```javascript
const handleClick = (e) => {
  setSuggestions([]);
  setValue(e.target.innerText);
  setSuggestionsActive(false);
};
```
To allow users to navigate between each of the suggestions and also select a suggestion using the keyboard we’ll use a `keyDown` event to listen for when either the up/down arrow and enter keys are pressed:

```javascript
const handleKeyDown = (e) => {
  // UP ARROW
  if (e.keyCode === 38) {
    if (suggestionIndex === 0) {
      return;
    }
    setSuggestionIndex(suggestionIndex - 1);
  }
  // DOWN ARROW
  else if (e.keyCode === 40) {
    if (suggestionIndex - 1 === suggestions.length) {
      return;
    }
    setSuggestionIndex(suggestionIndex + 1);
  }
  // ENTER
  else if (e.keyCode === 13) {
    setValue(suggestions[suggestionIndex]);
    setSuggestionIndex(0);
    setSuggestionsActive(false);
  }
};
```

For the actual suggestions we’ll create a `Suggestions` component:

```javascript
const Suggestions = () => {
  return (
    <ul className="suggestions">
      {suggestions.map((suggestion, index) => {
        return (
          <li
            className={index === suggestionIndex ? "active" : ""}
            key={index}
            onClick={handleClick}
          >
            {suggestion}
          </li>
        );
      })}
    </ul>
  );
};
```

This outputs the `suggestions` array into an unordered HTML list. Note we’ve added a conditional `active` class which will allow us to style the list item the user has selected using the up/down arrows on the keyword. You can add the following CSS to see this in action once the component is complete:

```css
.active {
  background: lightgray;
}
```

To complete the component update the `return` statement as follows:

```javascript
return (
  <div className="autocomplete">
    <input
      type="text"
      value={value}
      onChange={handleChange}
      onKeyDown={handleKeyDown}
    />
    {suggestionsActive && <Suggestions />}
  </div>
);
```
Here’s how the completed `AutoComplete` component should look:

```javascript
import { useState } from "react";

const AutoComplete = ({ data }) => {
    
  const [suggestions, setSuggestions] = useState([]);
  const [suggestionIndex, setSuggestionIndex] = useState(0);
  const [suggestionsActive, setSuggestionsActive] = useState(false);
  const [value, setValue] = useState("");

  const handleChange = (e) => {
    const query = e.target.value.toLowerCase();
    setValue(query);
    if (query.length > 1) {
      const filterSuggestions = data.filter(
        (suggestion) =>
          suggestion.toLowerCase().indexOf(query) > -1
      );
      setSuggestions(filterSuggestions);
      setSuggestionsActive(true);
    } else {
      setSuggestionsActive(false);
    }
  };

  const handleClick = (e) => {
    setSuggestions([]);
    setValue(e.target.innerText);
    setSuggestionsActive(false);
  };

  const handleKeyDown = (e) => {
    // UP ARROW
    if (e.keyCode === 38) {
      if (suggestionIndex === 0) {
        return;
      }
      setSuggestionIndex(suggestionIndex - 1);
    }
    // DOWN ARROW
    else if (e.keyCode === 40) {
      if (suggestionIndex - 1 === suggestions.length) {
        return;
      }
      setSuggestionIndex(suggestionIndex + 1);
    }
    // ENTER
    else if (e.keyCode === 13) {
      setValue(suggestions[suggestionIndex]);
      setSuggestionIndex(0);
      setSuggestionsActive(false);
    }
  };

  const Suggestions = () => {
    return (
      <ul className="suggestions">
        {suggestions.map((suggestion, index) => {
          return (
            <li
              className={index === suggestionIndex ? "active" : ""}
              key={index}
              onClick={handleClick}
            >
              {suggestion}
            </li>
          );
        })}
      </ul>
    );
  };

  return (
    <div className="autocomplete">
      <input
        type="text"
        value={value}
        onChange={handleChange}
        onKeyDown={handleKeyDown}
      />
      {suggestionsActive && <Suggestions />}
    </div>
  );
  
};

export default AutoComplete;
```

Finally we can update `App.js` to load the component and the data:

```javascript
import Autocomplete from "./AutoComplete";
import { autoCompleteData } from "./data.js";

function App() {
  return (
    <div className="App">
      <Autocomplete data={autoCompleteData} />
    </div>
  );
}

export default App;
```

That’s all for this tutorial, you should now have a working autocomplete search component that can easily be dropped into a React application. You can get the [full source code](https://github.com/w3collective/react-autocomplete-search) for this tutorial and all tutorials published on w3collective from [GitHub](https://github.com/w3collective/).