Search justacoding.blog. [Enter] to search. Click anywhere to close.

April 27th, 2022

Build A React Autocomplete Component

Autocomplete is a useful feature that’s also pretty fun to build in React.

If your website or application has a search facility, it may be a good idea to include some sort of autocomplete functionality to assist your users and help them find what they are looking for more easily.

To briefly summarize the functionality: the user starts to type in their search term. After a brief delay, “suggestions” are displayed beneath the search input — usually in the form of a dropdown.

The user can then click one of these items to navigate directly towards it.

Here’s a React Autocomplete component example over at JSFiddle.

Let’s take a look at how the component works!

The main component – Autocomplete

As you can see, this React component is relatively small, and it’s responsible for the entirety of the autocomplete functionality.

It consists of:

  • a search input field and associated functionality (the actual lookup)
  • a debounce mechanism
  • a means to “clear” the form and reset it to default
  • a loading state
  • an dropdown area to display results to the user

With that in mind, let’s take about each aspect of this particular autocomplete input component in turn.

The autocomplete search input

So this is a standard input field.

<input type="text" onChange={e => setInput(e.target.value)} value={input} />

On change, the idea is to trigger a network request. This network request would be responsible for performing a lookup in the database (or other data source) and return the results to the client.

As I’m not actually performing a network request here, for the sake of demonstration, you can see I’ve just returned a promise after doing a simple search/filter through some data in memory (the countries array).

The idea being to imitate a real network request (where you’d be searching from an API for instance).

The debounce mechanism

Debouncing is a great utility we can employ to help with our user experience.

In basic terms, to debounce just means to delay. You’ll find that a lot of autocomplete components utilize some sort of debouncing effect. The same goes for any kind of input that reacts directly to inputs on a per-input (ie. per key-press) basis.

In this case, we’re delaying the execution of our network request. We don’t want to trigger the network request on every single key-press, this is wasteful and inefficient.

Instead, it’s much better to trigger the network request once the user has actually finished typing.

We can use a debounce mechanism to achieve this, as demonstrated here:

  let timer = useRef()
  useEffect(() => {
    if (input && input.length > 0) {
      clearTimeout(timer.current)
      setLoading(true)
      
      timer.current = setTimeout(() => {
        search(input)
          .then(results => setResults(results))
          .catch(e => alert("error :("))
          .finally(() => setLoading(false))
      }, debounceTime)
    }
  }, [input])

How the debounce actually works

There’s a reference to a timer here.

Once input changes – we want to clear this timer and then set it off again.

If the timer doesn’t get cleared before it actually completes – our underlying functionality will be triggered.

So effectively, every key-press resets the timer, which is essence is “delaying” the final request. Once the user hasn’t pressed a key for the allocated amount of time (in this case, that’s denoted by debounceTime) we’ll take that as a signal to fire off our search request.

So it’s all about starting/stopping the underlying setTimeout, basically.

That’s the debounce functionality in a nutshell.

Clear the input

This part is simple enough.

  const clear = () => {
    setInput("")
    setResults([])
  }

We’ve got a “Clear” button that allows the user to reset the autocomplete input element to it’s initial/default state. This means we can effectively clear the autocomplete search input as well as the dropdown element containing the relevant results from the previous search.

This is a nice-to-have. In general, users will tend to find this type of functionality fairly useful, it just saves the extra time required to manually reset the search input field.

A loading state

So we can easily implement a loading mechanism through the use of a simple boolean in state.

const [loading, setLoading] = useState(false)

Once we trigger a network request — let the user know that the application is doing some work.

Once the request has completed (so once it’s either succeeded or errored in some way), clear this loading state accordingly.

This is the most simplistic implementation of a loading mechanism, and it’s perfectly sufficient for this occasion.

Generally speaking, it’s good to show a loading indicator of some sort for these kind of interactions. With autocomplete components in particular, it may not be initially apparent to the user that the search input is actually about to autocomplete (or make a request) — so the loading feedback is even more useful, here.

Displaying the results in a dropdown

Lastly, we obviously need somewhere to display our search results!

To begin with, we’ll just need an item in state — results.

const [results, setResults] = useState([])

Now, once we’ve executed our search request, we can set the resulting data from this request to our results variable in state.

With this in mind, the only thing remaining is to actually output these results in the user interface.

{!loading && input.length > 0 && (
  <div className="results">
    <div className="resultsCount">
      {results.length > 0 ? `${results.length} results found` : "No results found, please try again"}
    </div>
      
    {results.map(result => {
      return <div 
        className="result" 
        dangerouslySetInnerHTML={{__html: outputResult(input, result, shouldHighlightMatchedSearchTerm)}} 
      />
    })}
  </div>
)}

So this is a simple map that just outputs each (matched) array item in turn.

It’s pretty standard behaviour to do this within a dropdown beneath the main search input. You can of course experiment with different positions, but typically, this is what the users will likely expect of your autocompleting input element.

Highlighting the matched search term

This is a nice feature to include. It’s quite satisfying and in my opinion, it’s good for the user experience.

const outputResult = (input, result, shouldHighlightMatchedSearchTerm) => {
  if (!input || !result) {
    return ""
  }
  if (!shouldHighlightMatchedSearchTerm) {
    return result
  }
  const pattern = new RegExp(input, "g")
  return result.replace(pattern, "<b>" + input + "</b>");
}

So when we display a match — I’ve decided to make the “matched” text bold (using RegEx, to match all occurrences). This just gives the user an extra indicator as to why the given item is being displayed in the dropdown to begin with.

As always, it’s good to make these kind of extras configurable.

It’s possible to disable this functionality via props passed to the component, though in this case, it’s enabled by default.

How to improve the React autocomplete component

This React autocomplete input example is of course fairly basic. However, it provides a good opportunity to expand and build upon the existing functionality that’s already been set out.

Here are some ideas.

Add some nice styling!

This one goes without saying.

The current autocomplete component is a basic shell, why not add your own styling and see what you can come up with?

Return objects from the “backend” instead

This would allow you to output more than just a single search term per each result item. Expanding this autocomplete dropdown item can be done in many ways.

You could show an excerpt of the target page (if the result is a page), or display the category alongside the page title. There are a lot of different things you can do in this regard.

There’s also the potential for images, depending on how bold you’d like your autocomplete component to become!

Limit or shorten the results

You’ll likely want to handle what should happen when there are many results (too many results to display).

In this case, one idea would be to trim down the results – so only show the first 10 (which could be configured via prop). And then have a “view more results” button, which would presumably link off to somewhere else in the website or application.

It may be interesting to also play around with some sort of pagination for the autocomplete results.

So there could be a button (“view next page”, or something similar) that would allow the user to cycle through the results within the autocomplete dropdown itself.

This React autocomplete example is definitely very basic; but by implementing ideas (like the ones above) you can really improve it and enhance its functionality and overall usefulness for your users.

More code examples and components like this one

I hope you’ve enjoyed this article and the accompanying React autocomplete input functionality example.

If so, why not check out the other code examples on justacodingblog?

They each work in the same way. A relatively simple script or component with an accompanying article that attempts to provide a decent overview of what’s going on.

Thanks for reading!

Have any questions? Ping me over at @justacodingblog
← Back to blog