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
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",
];
Code language: JavaScript (javascript)
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;
Code language: JavaScript (javascript)
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("");
Code language: JavaScript (javascript)
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);
}
};
Code language: JavaScript (javascript)
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);
};
Code language: JavaScript (javascript)
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);
}
};
Code language: JavaScript (javascript)
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>
);
};
Code language: JavaScript (javascript)
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;
}
Code language: CSS (css)
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>
);
Code language: JavaScript (javascript)
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;
Code language: JavaScript (javascript)
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;
Code language: JavaScript (javascript)
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.