Tutorial

How To Build an Autocomplete Component in React

Updated on December 22, 2020
authorauthor

joshtronic and Natalia Vargas-Caba

How To Build an Autocomplete Component in React

Introduction

Autocomplete is a feature in which an input field suggests a word based on user input. This helps improve the user experience in your application, such as cases where a search is necessary.

In this article, you will examine how to build an autocomplete component in React. You will work with a fixed list of suggestions, event binding, understanding keyboard codes, and operate state management.

Prerequisites

To complete this tutorial, you will need the following:

  • An understanding of React is required. To learn more about React, check out the How To Code in React series.

Step 1 — Building Your Autocomplete Component

The Autocomplete component is where you will craft the functionality in the autocomplete feature.

Create an Autocomplete.js file, and import React and instantiate an Autocomplete class:

Autocomplete.js
import React, { Component, Fragment } from "react";
import './styles.css'

class Autocomplete extends Component {
  constructor(props) {
    super(props);
    this.state = {
      activeSuggestion: 0,
      filteredSuggestions: [],
      showSuggestions: false,
      userInput: ""
    };
  }

On your state, the activeSuggestion property defines the index of a selected suggestion. The property filteredSuggestions, set to an empty array, matches the user’s input. The showSuggestions property will determine whether or not the the suggestion list appears, and the userInput property assigns itself an empty string for accepting a word from a user’s input.

With your class started and state set, let’s look at the methods to apply in the component.

In your Autocomplete.js file, define an onChange method and update your state:

Autocomplete.js
  onChange = e => {
    const { suggestions } = this.props;
    const userInput = e.currentTarget.value;

    const filteredSuggestions = suggestions.filter(
      suggestion =>
        suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
    );

    this.setState({
      activeSuggestion: 0,
      filteredSuggestions,
      showSuggestions: true,
      userInput: e.currentTarget.value
    });
  };

The method onChange will fire when the user changes the input value. On each change, the method will filter into the suggestion list and return suggestions that do not contain the user’s input. Once the filter runs through, the .setState() method will revise your state’s userInput property to contain a value, set your showSuggestions boolean to allow suggestions to show, and reset the activeSuggestion property on each method call.

The onClick event will invoke when the user clicks on a suggestion. In your Autocomplete.js file, declare an onClick method and implement a .setState() method:

Autocomplete.js
  onClick = e => {
    this.setState({
      activeSuggestion: 0,
      filteredSuggestions: [],
      showSuggestions: false,
      userInput: e.currentTarget.innerText
    });
  };

The .setState() method will update the user’s input and reset your state’s properties.

The onKeyDown method will engage when the user presses a key down. In your Autocomplete.js file, declare an onKeyDown method and set several conditionals:

Autocomplete.js
  onKeyDown = e => {
    const { activeSuggestion, filteredSuggestions } = this.state;

    if (e.keyCode === 13) {
      this.setState({
        activeSuggestion: 0,
        showSuggestions: false,
        userInput: filteredSuggestions[activeSuggestion]
      });
    } else if (e.keyCode === 38) {
      if (activeSuggestion === 0) {
        return;
      }
      this.setState({ activeSuggestion: activeSuggestion - 1 });
    }
    // User pressed the down arrow, increment the index
    else if (e.keyCode === 40) {
      if (activeSuggestion - 1 === filteredSuggestions.length) {
        return;
      }
      this.setState({ activeSuggestion: activeSuggestion + 1 });
    }
  };

Once you’ve stored your properties activeSuggestion and filteredSuggestion in a deconstructed object, the conditionals will check if the key the user pressed down matches the key codes. The first conditional will check if the key code matches 13, the enter key, and run the .setState() method to update the userInput property and close the suggestions list. If the user pressed the up arrow, the key code 38, the conditional will decrement the index of the activeSuggestion property, and return nothing if the index is zero. If the user pressed the down arrow, the key code 40, the conditional will increment the index in the activeSuggestion property, and return nothing if the index matches the length of the filteredSuggestions property.

With your methods now complete, let’s navigate to applying your render lifecycle method.

In your Autocomplete.js file, set a render() statement, and define your methods and state in a deconstructed object:

Autocomplete.js
  render() {
    const {
      onChange,
      onClick,
      onKeyDown,
      state: {
        activeSuggestion,
        filteredSuggestions,
        showSuggestions,
        userInput
      }
    } = this;

    let suggestionsListComponent;

The variable suggestionsListComponent has no defined value as you will assign them in the conditionals below:

Autocomplete.js
    if (showSuggestions && userInput) {
      if (filteredSuggestions.length) {
        suggestionsListComponent = (
          <ul class="suggestions">
            {filteredSuggestions.map((suggestion, index) => {
              let className;

              // Flag the active suggestion with a class
              if (index === activeSuggestion) {
                className = "suggestion-active";
              }
              return (
                <li className={className} key={suggestion} onClick={onClick}>
                  {suggestion}
                </li>
              );
            })}
          </ul>
        );
      } else {
        suggestionsListComponent = (
          <div class="no-suggestions">
            <em>No suggestions available.</em>
          </div>
        );
      }
    }

The first conditional will check if the values on the properties showSuggestions and userInput exists, while the following condition will check for the length of the filteredSuggestions property. If the conditionals are met, the suggestionsListComponent variable assigns itself the value of iterating through the filteredSuggestions property and flag an active suggestion with a class name if the index matches the value in the activeSuggestion property. The suggestionsListComponent variable will return an ordered list of the suggestions upon execution of the onClick method, and assign each suggestion a class name. If the values on the properties showSuggestions and userInput do not exist, a text will appear to indicate no there are no suggestions available.

If the user does not meet the conditions listed, the render() lifecycle method will return a React Fragment to apply the input field and invoke the methods without adding extra nodes to the document object model:

Autocomplete.js
    return (
      <Fragment>
        <input
          type="text"
          onChange={onChange}
          onKeyDown={onKeyDown}
          value={userInput}
        />
        {suggestionsListComponent}
      </Fragment>
    );
  }
}

export default Autocomplete;

Now that you’ve developed your Autocomplete component, export the file combine its functionality in another component.

Step 2 — Joining Your React Project

The App component is where you will display the functionality in your Autocomplete component. In your index.js file, declare an App component and import your Autocomplete component:

index.js
import React from "react";
import Autocomplete from "./Autocomplete";

function App() {
  return (
    <div>
      <h1>React Autocomplete Demo</h1>
      <h2>Start typing and experience the autocomplete wizardry!</h2>
      <Autocomplete suggestions={"Oranges", "Apples", "Banana", "Kiwi", "Mango"]}/>
    </div>
  );
}

export default App

The return statement in your App component accepts the Autocomplete component with the fixed list of suggestions.

Step 3 — Styling Your Autocomplete Component

To finish your Autocomplete component, add styling with CSS to position and color your application and input field.

Create a styles.css file and set CSS rules to shape your Autocomplete component:

styles.css
body {
  font-family: sans-serif;
}

input {
  border: 1px solid #999;
  padding: 0.5rem;
  width: 300px;
}

.no-suggestions {
  color: #999;
  padding: 0.5rem;
}

.suggestions {
  border: 1px solid #999;
  border-top-width: 0;
  list-style: none;
  margin-top: 0;
  max-height: 143px;
  overflow-y: auto;
  padding-left: 0;
  width: calc(300px + 1rem);
}

.suggestions li {
  padding: 0.5rem;
}

.suggestion-active,
.suggestions li:hover {
  background-color: #008f68;
  color: #fae042;
  cursor: pointer;
  font-weight: 700;
}

.suggestions li:not(:last-of-type) {
  border-bottom: 1px solid #999;
}

With your CSS rules constructed, import the file into your Autocomplete.js file to apply your styles.

Conclusion

React offers measures to construct the autocomplete functionality available in one component with built-in and custom methods to interact with the user.

Check out how the Autocomplete component operates in CodeSandbox.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
joshtronic

author



Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
4 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Good Day,

Just wanted confirm with your code the following:

onKeyDown = e => {
  const { activeSuggestion, filteredSuggestions } = this.state;

  if (e.keyCode === 13) {
    this.setState({
      activeSuggestion: 0,
      showSuggestions: false,
      userInput: filteredSuggestions[activeSuggestion]
    });
  } else if (e.keyCode === 38) {
    if (activeSuggestion === 0) {
      return;
    }
    this.setState({ activeSuggestion: activeSuggestion - 1 });
  }
  // User pressed the down arrow, increment the index
  else if (e.keyCode === 40) {
    if (activeSuggestion - 1 === filteredSuggestions.length) {
      return;
    }
    this.setState({ activeSuggestion: activeSuggestion + 1 });
  }
};

Shouldn’t the logic at the keyCode === 40 be activeSuggestion + 1 === filteredSuggestions.length as you increase your position when pressing the down arrow.

Please let me know if I am correct.

Great stuff exactly what I was looking for!

suggestion list scrolling is not happening with keyboard up and down arrow keys. please update code. Thanks

Hi Josh, this is great, exactly what I was looking for! I have a follow-up question - in case of a larger textarea, I would like the auto-complete to show somewhere below or above the current cursor position (not under the input box) - any tips on how to achieve this? Thanks

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.