I am going to show you guys how to make a basic search engine that would enable anyone to search for specific data in your table that you’ve built using any package or library in React. This method is a generic one and would work with any type of table that involves feeding a data source to it in order to render the rows. I’m going to go a bit slow with the explanation as things start to get rough-rough and we start building our own small search engine, so feel free to have a look at the following sandbox where I have implemented the whole thing : Give the whole story a read as I will be talking deeply about all the code and the custom hook involved. It doesn’t matter which library you are using to build your tables. I will be using antd, which is a React library used to design complex interfaces that involve form control and large tables. For the data, I have used the users from . You can have a look at the data while you are going through the code in order to understand stuff better. https://jsonplaceholder.typicode.com/users Let’s get started. The file structure is as follows: The simply renders the App component. I’ll be going through the App component first: index.js React, { useState } ; { Table, Input } ; axios ; { userColumns } ; { useTableSearch } ; { Search } = Input import from "react" import from "antd" import from "axios" import from "./columns" import from "./useTableSearch" const Here, is going to help us fetch data from the jsonplaceholder’s users API. The are the columns we are going to use in our users table. You can go through them on your own in the sandbox. The is a custom hook we are going to use to get our data filtered depending on our search query. axios userColumns useTableSearch fetchUsers = () => { { data } = axios.get( ); { data }; }; const async const await "https://jsonplaceholder.typicode.com/users/" return This is the function which we are going to pass to our hook so that it can fetch the data of all the users which will then be used as the data on which the search can be performed. fetchUsers { [searchVal, setSearchVal] = useState( ); { filteredData, loading } = useTableSearch({ searchVal, : fetchUsers }); export default ( ) function App const null const retrieve This is the body of our App component. First we will define our local state. is going to hold the value of the user input from the search bar, and will help us update it’s value inside an event handler that we are going to attach to our search bar element. We set it’s initial value to . searchVal setSearchVal onChange null The next line shows how we are going to utilize the custom hook to get our filtered data based on the . We are going to pass the and the function, that we talked about earlier, as the parameter, which will be used to fetch the data inside the hook so that the can be used to filter that data. The hook is going to return the along with a parameter that can be used to display a spinner while the data is being fetched for the first time. useTableSearch searchVal searchVal fetchUsers retrieve searchVal filteredData loading ( <Search onChange={e => setSearchVal(e.target.value)} placeholder="Search" enterButton style={{position: 'sticky', top: '0', left: '0'}} /> <br /> <br /> <Table dataSource={filteredData} columns={userColumns} loading={loading} pagination={false} rowKey='name' /> </> ); } return <> I am going to use the antd component to render the search bar, you can use any type of text input to do the same. The function updates the value of the state variable whenever the user types something. This leads to the re-render of the whole component and the updated is passed to our custom hook which in turn returns the . This is then passed as the to our table. Search onChange searchVal searchVal filteredData dataSource Now that you have reached this far, you can go ahead and start using the hook inside your personal projects. I will now go through the hook and explain how the search engine is working. So if you’re interested to know how stuff works, continue reading! The Custom Hook The beauty of a custom hook in React is it’s reusability and ease of use. I am personally working on a project that includes a large number of tables. This hook has been very useful to add the same functionality to all my tables. Let’s go through the code { useState, useEffect } ; useTableSearch = { [filteredData, setFilteredData] = useState([]); [origData, setOrigData] = useState([]); [searchIndex, setSearchIndex] = useState([]); [loading, setLoading] = useState( ); import from "react" export const ( ) => { searchVal, retrieve } const const const const true First, we will be defining our states. is an array of filtered objects, is the original data that we will fetch using our method that has been passed on to the hook from the component, is the search index that we will build using our . This will be used to filter the data whenever changes. Finally, we have which tells us when our data is being fetched. filteredData origData retrieve App searchIndex origData searchIndex searchVal loading useEffect( { setLoading( ); crawl = { (!allValues) allValues = []; ( key user) { ( user[key] === ) crawl(user[key], allValues); allValues.push(user[key] + ); } allValues; }; fetchData = () => { { : users } = retrieve(); setOrigData(users); setFilteredData(users); searchInd = users.map( { allValues = crawl(user); { : allValues.toString() }; }); setSearchIndex(searchInd); (users) setLoading( ); }; fetchData(); }, [retrieve]); => () true const ( ) => user, allValues if for var in if typeof "object" else " " return const async const data await const => user const return allValues if false This is our first call, it performs 2 major tasks: useEffect To fetch the original data and store it in our local state. users To generate a Search Index using the data, which can later be used to perform the actual filtering, and to also store it in our local state. It gets called only when the function changes. So, most of the times it only gets called when the component is mounted i.e. once. retrieve Now, what is a search index? In layman’s terms, a search index is a modified form of the original data on which we want to perform the search on, such that the modification makes the operation of searching easier / more viable. So, how can we modify our data in a way that makes it possible to search for a value over the data? One easy method which we can use, and in-fact I have used, is to crawl ( or iterate ) over all the values of every that exists in our array of objects and convert every object to a single string. This will enable us to use the function in JavaScript to filter our data based on the as the function returns a positive integer ( i.e. the index of the sub-string ) if the string passed to it is a sub-string of the given string and -1 otherwise. Lets dive in deep. users user users user indexOf() searchVal indexOf() The Crawler The first thing to keep in mind is that the crawl function is called on each and every object that is present in the array and returns an array of all the values of that object. Later we convert that array to a single string using the function that concatenates all the values present in the array. This is done using a simple recursive function which checks whether it needs to crawl further or not based on the value at every key, let’s take an example : user users toString() allValues The crawler will start from the and push it to array. It will keep on pushing the values until it encounters the key, in which case it needs to crawl further inside to get the values, it will do exactly that. The crawl function will be recursively called for the object and all the values will be pushed to . id allValues address address address allValues Finally we will have an array of all the values of the which we will convert into a string using . This will be done for all the and will together constitute our . user toString() users searchIndex Now, in our function, we use the parameter, that has been passed on from the component, to fetch all the . After that we generate our by mapping over all our and crawling on each one of them. We will make sure to to before we start fetching the data and then to when the data has been fetched. fetchData retrieve App users searchIndex users setLoading true false Now that we have our , let’s go through the filtering part. searchIndex useEffect( { (searchVal) { reqData = searchIndex.map( { (user.allValues.toLowerCase().indexOf(searchVal.toLowerCase()) >= ) origData[index]; ; }); setFilteredData( reqData.filter( { (user) ; ; }) ); } setFilteredData(origData); }, [searchVal, origData, searchIndex]); { filteredData, loading }; }; => () if const ( ) => user, index if 0 return return null => user if return true return false else return Notice that we have the as a dependency for our hook. This hook will be called every-time the user input changes. searchVal We map over all the strings present in our . Each string in our corresponds to an original present in our at the same as the string. Thus, by using the function, if we find that the is a of a string present at say - of our , we can say that the present at of our should be a part of the . searchIndex searchIndex user origData index indexOf() searchVal substring index=5 searchIndex user index=5 origData filteredData This is how the filtering takes place. If the is not a of a string in the , we return for the corresponding value. Finally, we filter our data to remove all those values and we set our to the updated value in our local state. This is what is returned to the component along with . searchVal substring searchIndex null index null filteredData App loading Conclusion We used a custom hook in React to filter our data using a search query, and then used that filtered data to render our table. In the process of filtering our data we made a small object crawler and a search index to help us in the process. This was how you can build a search engine for your table. That’s all I have for now. I hope you guys enjoyed the explanation. Thanks for reading! Godspeed.