Building a Complete Search Autocomplete Feature in React

Autocomplete search is a common feature in modern applications. It helps users quickly find what they need by showing suggestions while they type. In this article, we will walk through the full implementation of an autocomplete component built with React. We will explain each line of code, every state variable, and the logic behind the behavior.
We will work with two components:
- SearchAutocomplete — the main logic (fetching data, filtering, handling input)
- Suggestions — a presentation component that displays the suggestion list
Let’s go through the functionality in depth.
The SearchAutocomplete Component
import { useEffect, useState } from 'react'
import Suggestions from './suggestions'
We import two important React hooks:
- useState → used to store and update values inside the component
- useEffect → used to run code when the component mounts
We also import the Suggestions component that we will use later.
Declaring All State Variables
const [loading, setLoading] = useState(false)
const [users, setUsers] = useState([])
const [error, setError] = useState(null)
const [searchParam, setSearchParam] = useState('')
const [showDropdown, setShowDropdown] = useState(false)
const [filteredUsers, setFilteredUsers] = useState([])
Each state variable has a specific purpose:
loading
- Starts as false
- Becomes true while data is being fetched from the API
- Helps us display a loading message
users
- Will store the list of all user first names fetched from the remote API
error
- Stores any error that occurs during the API request
- Helps us display an error message
searchParam
- Stores the text typed inside the search input
- Makes the input a controlled input
showDropdown
- Controls whether the suggestions dropdown is visible
- Becomes true only when the user types more than 1 character
filteredUsers
- Stores the users that match the search text
- This list is displayed inside the dropdown
These state variables together allow us to handle:
- User input
- API data
- UI visibility
- Error handling
The Input Change Handler
function handleChange(event) {
const query = event.target.value.toLowerCase()
setSearchParam(query)
if (query.length > 1) {
const filteredData =
users && users.length
? users.filter(item => item.toLowerCase().indexOf(query) > -1)
: []
setFilteredUsers(filteredData)
setShowDropdown(true)
} else {
setShowDropdown(false)
}
}
This function runs every time the user types in the input field.
Let’s break down each step:
Step 1: Read the Input Value
const query = event.target.value.toLowerCase()
We convert it to lowercase to allow case-insensitive search.
Step 2: Update the searchParam
setSearchParam(query)
This makes the input controlled by React.
Step 3: Only search if the user typed more than 1 character
if (query.length > 1) {
This prevents showing suggestions for short input like “a”.
Step 4: Filter the User List
const filteredData = users.filter(item =>
item.toLowerCase().indexOf(query) > -1
)
Here is how the filtering works:
- Convert each username to lowercase
- Check if the typed text exists inside the username
- Keep only the matching names
Step 5: Update States
setFilteredUsers(filteredData)
setShowDropdown(true)
Step 6: Hide Suggestions if the Input Is Too Short
else {
setShowDropdown(false)
}
Handling Click on a Suggestion
function handleClick(event) {
setShowDropdown(false);
setSearchParam(event.target.innerText);
setFilteredUsers([]);
}
When the user clicks a name:
- Close the dropdown
- Put the clicked name into the input field
- Clear the list of suggestions
This gives the effect of selecting a suggestion and confirming the value.
Fetching Users from the API
async function fetchListOfUsers () {
try {
setLoading(true)
const response = await fetch('https://dummyjson.com/users')
const data = await response.json()
if (data && data.users && data.users.length) {
setUsers(data.users.map(userItem => userItem.firstName))
setLoading(false)
setError(null)
}
} catch (error) {
setLoading(false)
setError(error)
}
}
1. Set loading to true
setLoading(true)
2. Fetch user data
const response = await fetch('https://dummyjson.com/users')
const data = await response.json()
3. Store the first names only
setUsers(data.users.map(userItem => userItem.firstName))
4. Reset loading and error states
setLoading(false)
setError(null)
5. Error handling
If something goes wrong, store the error and stop loading.
Running the API Call with useEffect
useEffect(() => {
fetchListOfUsers()
}, [])
- Runs once when the component first loads
- Works like a componentDidMount in class components
This ensures the API call runs only one time.
Rendering the UI
if(error){
return <h1>Error</h1>;
}
If something went wrong, show an error message.
Main Render Section
<div className='search-autocomplete-container'>
{loading ? (
<h1>Loading Data. Please wait</h1>
) : (
<input
value={searchParam}
name='search-users'
placeholder='Search Users here...'
onChange={handleChange}
/>
)}
What this does:
- If data is still loading → show “Loading Data”
- Otherwise → show the input field
Showing the Suggestions
{showDropdown && <Suggestions handleClick={handleClick} data={filteredUsers} />}
This line:
- Only shows the dropdown if showDropdown is true
- Passes the list of filtered users
- Passes the click handler
The Suggestions Component
export default function Suggestions ({ data, handleClick }) {
return (
<ul>
{data && data.length
? data.map((item, index) => (
<li key={index} onClick={handleClick}>
{item}
</li>
))
: null}
</ul>
)
}
This component is simple:
- Receives the filtered users
- Displays each one in a <li>
- Calls handleClick when clicked
It is a pure UI component with no logic.
Final Result
With both components combined, we now have:
- A working autocomplete feature
- Real user data from an API
- Controlled input
- Case-insensitive filtering
- A dropdown that hides or shows based on input
- Click-to-select behavior
The component is clean, efficient, and easy to extend.
Happy coding !!
Building a Complete Search Autocomplete Feature in React was originally published in Javarevisited on Medium, where people are continuing the conversation by highlighting and responding to this story.
This post first appeared on Read More

