Adding the Search Functionality
Learn how to add the search functionality in Next.js.
Adding the search functionality
In this lesson, we’ll create a search page where users will be able to query the database for meals and find what they’re looking for.
To add the search functionality to our application, we need to add a few things to our page. Let’s discuss what we’ll have to do:
Add a text field where the user can enter what they want to search.
Send the user’s value to our API and fetch the results.
Display the fetched results on the web page for the user(s).
Now that we know what we need to do, let’s get started.
Fetching results from the API
We’ll have to create functions that call the API endpoint with the value that the user enters. We’re creating the functions first so that we can call them directly when creating the text field and not have to go back and add code there later.
To send the value to the API, we need to create two functions: one to handle the button click and one to handle the “Enter” key press event. Let’s see what these functions look like:
const [options, setOptions] = useState([])// Button click handlerconst getSearchResults = async () => {const data = await (await fetch(`https://www.themealdb.com/api/json/v1/1/search.php?s=${searchVal}`)).json()setOptions(data.meals)}// Enter press handlerconst enterPressHandler = (event) => {if (event.key === 'Enter') {event.preventDefault()getSearchResults()}}
The getSearchResults
function is the one that calls the API endpoint and is called directly when the user clicks the “Search” button on the screen. Meanwhile, the enterPressHandler
is called when the user presses a button on the keyboard, and it calls the getSearchResults
function to get the results of the search.
In the enterPressHandler
, we’ve added a check that ensures that nothing happens except when the user presses the “Enter” key because that isn’t a part of our requirements.
Creating the text field
Now that we have our event handlers ready, let’s create a text field where the user can input the meal they want to search. For that, we’re going to use the TextField
component from Material UI to make our life a little easier. We’ll also add the event handlers to the text field so that we can get the results.
export default function Home() {const [searchVal, setSearchVal] = useState("")return(<><Head><title>Home | Recipes App</title><meta name="description" content="Homepage of the Recipes application" /></Head><Header /><main className={styles.main}><div className={styles.searchbarDiv}><TextField fullWidthplaceholder='Search...'onChange={(event) => {setSearchVal(event.target.value)}}onKeyDown={enterPressHandler}InputProps= {{endAdornment:<IconButton onClick={getSearchResults} ><SearchOutlined /></IconButton>}}/></div></main></>)}
Notice the important details between lines 15–19:
Line 15: The first thing we add is the
onChange
parameter, which sets the value ofsearchVal
whenever the value in the text field is changed.Line 16: We add the
onKeyDown
parameter, which calls theenterPressHandler
that we created previously. With this, whenever the user presses any key, this function is called.Line 17–19: We’ve got the
InputProps
parameter, to which we add anIconButton
with aSearchOutlined
icon to the end of the text field. The user can click this icon to call the search. TheIconButton
calls thegetSearchResults
function we defined earlier.
With the code above, we receive the results from the API, which we need to display to the user.
Displaying the search results
To display the search results, we’ll add the same cards that we used previously in our meals page. We've already created a component to display the results, so we just have to add a single line of code to call the component.
<div className={styles.searchResult}>{options == null ? <div>No results found</div> : <MealCard meals={options} />}</div>
We’ve included a ternary operator to validate that the search returns at least one meal. If we don’t add this check, the application will crash because we’ll be trying to access the map
function on a null
object instead of an array.
We’ve now completed all the things that we need to complete our search functionality. Let’s see it in action now:
import { Grid, Card, Typography } from '@mui/material'; import React from 'react' import Image from 'next/image'; import styles from '../styles/Home.module.css' import { useRouter } from 'next/router'; const MealCard = (props) => { const {meals} = props const router = useRouter(); return ( <Grid container direction="row" alignItems="center" justifyContent="center" spacing={1} sx = {{flex: "wrap"}}> { meals.map((meal) => <Grid item key={meal.idMeal} onClick = {() => router.push(`/meals/${meal.idMeal}`)} > <Card variant='outlined' className={styles.card}> <Grid container direction='column' justifyContent='center' alignItems='center' > <Grid item className={styles.imgContainer} > <Image src = {meal.strMealThumb} alt={meal.strMeal} layout="fill" objectFit="cover" quality={100} /> </Grid> <Grid item> <Typography variant='body1'> {meal.strMeal} </Typography> </Grid> </Grid> </Card> </Grid> ) } </Grid> ) } export default MealCard;
We also add a randomFinder
function to our page, as shown in lines 25–28. The function sends a request to the “Lookup a single random meal” endpoint of the API, which returns a random meal from the database. To use this function, we create a <div>
element in line 49 that the user can click to find a random meal.
We have now completed the frontend of our application, and we can move forward with deploying it.