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:

  1. Add a text field where the user can enter what they want to search.

  2. Send the user’s value to our API and fetch the results.

  3. 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:

Press + to interact
const [options, setOptions] = useState([])
// Button click handler
const 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 handler
const 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.

Press + to interact
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 fullWidth
placeholder='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 of searchVal whenever the value in the text field is changed.

  • Line 16: We add the onKeyDown parameter, which calls the enterPressHandler 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 an IconButton with a SearchOutlined icon to the end of the text field. The user can click this icon to call the search. The IconButton calls the getSearchResults 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.

Press + to interact
<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;
Putting together our search functionality

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.