Autocomplete fields are must-have user interface components that enhance the overall user experience of your app. They also help minimize redundant spelling mistakes when users search for specific terms. Since accessing client-side storage is incredibly fast, your app's performance will also improve.
In this guide, you will learn how to create an autocomplete field from scratch and store search suggestions locally in the browser.
Client-side storage is one of the most useful and futuristic features of HTML5 on the browser. It helps shift the processing load from the server session to client-side variables.
Types of Client-side storage include:
Local storage will be used for this guide, as it has a straightforward API compared to the other types of storage.
Using the localStorage
API, you can access local storage for a particular domain or website. Data is stored in a key-value pair format in local storage.
The following code snippet accesses the local storage and adds a data item to it using the localStorage.setItem()
method.
1localStorage.setItem("Name", "John Doe");
The first argument is the name of the key, and the second argument is its corresponding value.
To retrieve an item from local storage, use the localStorage.getItem()
method and pass the key as an argument.
1localStorage.getItem("Name");
Now you can start working on the AutoComplete
component. As you can see below, you need to have three properties in the state object. The inputValue
property will hold the value of the text input, the filteredOptions
property will hold the actual filtered result, and the options
property will have all the available options that were previously typed in an array.
1class AutoComplete extends Component {
2 constructor(props) {
3 super(props);
4 this.state = {
5 inputValue: "",
6 filteredOptions: [],
7 options: localStorage.getItem("autocompleteOptions")
8 ? JSON.parse(localStorage.getItem("autocompleteOptions"))
9 : [],
10 };
11
12 // ...
13 }
14
15 render() {
16 // ...
17 }
18}
Inside the render()
method, display the input field and add the logic to show the results.
1class AutoComplete extends Component {
2 constructor(props) {
3 // ...
4 }
5
6 render() {
7 return (
8 <div className="autocomplete-container">
9 <input
10 type="text"
11 value={this.state.inputValue}
12 onChange={this.handleInputChange}
13 onKeyDown={this.handleKeyDown}
14 />
15 <ul className="autocomplete-results">
16 {this.state.inputValue.length > 0 &&
17 this.state.filteredOptions.map((option) => (
18 <li key={option}>{option}</li>
19 ))}
20 </ul>
21 </div>
22 );
23 }
24}
In the handleInputChange()
method, set the inputValue
in the state and filter out the options using the Array.prototype.filter()
method. To determine if the term entered in the input exists in the available options or not, you can use the indexOf
method.
The indexOf()
method returns the index or the position value within the calling String
object of the first occurrence of the specified substring or value. It returns -1
if the value is not found in the string.
In the handleKeyDown()
method, add the entered value to the list of available options when the user hits the enter key on the keyboard. The keyCode
for the enter key is 13, so you can check if the keyCode
of the entered value is equal to 13 inside the function body using the native event
object.
1class AutoComplete extends Component {
2 constructor(props) {
3 // ...
4 }
5
6 handleInputChange(e) {
7 const userInput = e.target.value;
8 this.setState({ inputValue: userInput });
9 const filteredOptions = this.state.options.filter(
10 (option) => option.toLowerCase().indexOf(userInput.toLowerCase()) > -1
11 );
12 this.setState({ filteredOptions });
13 }
14
15 handleKeyDown(e) {
16 if (e.keyCode === 13) {
17 const userInput = e.target.value;
18 this.addOption(userInput);
19 this.setState({ inputValue: "" });
20 }
21 }
22
23 render() {
24 // ...
25 }
26}
In the addOption()
method, set the entered value in the options
state and immediately store the updated options
state in local storage. Don't forget to covert the array to string, as local storage does not support storing objects.
1class AutoComplete extends Component {
2 constructor(props) {
3 // ...
4 }
5
6 addOption(option) {
7 this.setState({ options: [...this.state.options, option] }, () => {
8 localStorage.setItem(
9 "autocompleteOptions",
10 JSON.stringify(this.state.options)
11 );
12 });
13 }
14
15 // ...
16
17 render() {
18 // ...
19 }
All right, so now that the component is ready, add some styling to make it look pretty as no one likes to see the default browser styling. You can either copy the CSS code below or add the styling yourself.
1.App {
2 font-family: sans-serif;
3 text-align: center;
4}
5
6.autocomplete-container {
7 width: 450px;
8 margin: auto;
9}
10
11.autocomplete-container input {
12 padding: 10px 18px;
13 font-size: 20px;
14 width: 100%;
15 border: 2px solid #999;
16 border-radius: 6px;
17}
18
19ul.autocomplete-results {
20 margin: 0;
21 margin-top: 8px;
22 list-style-type: none;
23 padding: 0;
24}
25
26ul.autocomplete-results li {
27 text-align: left;
28 padding: 10px 18px;
29 margin-bottom: 4px;
30 width: 100%;
31 box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.08);
32 border: 1px solid #eee;
33 background: #fefefe;
34}
Below you can find the complete code for the AutoComplete
component for your reference.
1import React, { Component } from "react";
2
3class AutoComplete extends Component {
4 constructor(props) {
5 super(props);
6 this.state = {
7 inputValue: "",
8 filteredOptions: [],
9 options: localStorage.getItem("autocompleteOptions")
10 ? JSON.parse(localStorage.getItem("autocompleteOptions"))
11 : [],
12 };
13
14 this.addOption = this.addOption.bind(this);
15 this.handleInputChange = this.handleInputChange.bind(this);
16 this.handleKeyDown = this.handleKeyDown.bind(this);
17 }
18
19 addOption(option) {
20 this.setState({ options: [...this.state.options, option] }, () => {
21 localStorage.setItem(
22 "autocompleteOptions",
23 JSON.stringify(this.state.options)
24 );
25 });
26 }
27
28 handleInputChange(e) {
29 const userInput = e.target.value;
30 this.setState({ inputValue: userInput });
31 const filteredOptions = this.state.options.filter(
32 (option) => option.toLowerCase().indexOf(userInput.toLowerCase()) > -1
33 );
34 this.setState({ filteredOptions });
35 }
36
37 handleKeyDown(e) {
38 if (e.keyCode === 13) {
39 const userInput = e.target.value;
40 this.addOption(userInput);
41 this.setState({ inputValue: "" });
42 }
43 }
44
45 render() {
46 return (
47 <div className="autocomplete-container">
48 <input
49 type="text"
50 value={this.state.inputValue}
51 onChange={this.handleInputChange}
52 onKeyDown={this.handleKeyDown}
53 />
54 <ul className="autocomplete-results">
55 {this.state.inputValue.length > 0 &&
56 this.state.filteredOptions.map((option) => (
57 <li
58 key={option}
59 onClick={() =>
60 this.setState({ inputValue: option, filteredOptions: [] })
61 }
62 >
63 {option}
64 </li>
65 ))}
66 </ul>
67 </div>
68 );
69 }
70}
71
72export default AutoComplete;
So that's it from this guide. You have successfully managed to create the perfect AutoComplete
component. You can go further with this component by combining server-side results with the client-side results using ajax calls. Proper state management and implementation using client-side storages can make your app amazingly fast and will give your users a much better user experience.