DEV Community

Michael Burrows
Michael Burrows

Posted on • Originally published at w3collective.com

Build a custom React autocomplete search component

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:

npx create-react-app react-autocomplete-search
Enter fullscreen mode Exit fullscreen mode

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:

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

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

import { useState } from "react";

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

export default AutoComplete;
Enter fullscreen mode Exit fullscreen mode

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

const [suggestions, setSuggestions] = useState([]);
const [suggestionIndex, setSuggestionIndex] = useState(0);
const [suggestionsActive, setSuggestionsActive] = useState(false);
const [value, setValue] = useState("");
Enter fullscreen mode Exit fullscreen mode
  • 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:

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);
  }
};
Enter fullscreen mode Exit fullscreen mode

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:

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

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:

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);
  }
};
Enter fullscreen mode Exit fullscreen mode

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

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

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:

.active {
  background: lightgray;
}
Enter fullscreen mode Exit fullscreen mode

To complete the component update the return statement as follows:

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

Here’s how the completed AutoComplete component should look:

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;
Enter fullscreen mode Exit fullscreen mode

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

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

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

export default App;
Enter fullscreen mode Exit fullscreen mode

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 for this tutorial and all tutorials published on w3collective from GitHub.

Top comments (1)

Collapse
 
abdullahsaad5 profile image
Syed Abdullah Saad

Great article but I think you can do it much more easily with something like datalist.