Building a Recipe App with React and Spoonacular API

In this tutorial, we will learn how to build a Recipe App using React and the Spoonacular API. React is a popular JavaScript library for building user interfaces, and the Spoonacular API provides access to a vast collection of recipes and cooking information. By the end of this tutorial, you will have a fully functional Recipe App that allows users to search for recipes, view recipe details, and add recipes to their favorites.

building recipe app react spoonacular api

Introduction

What is React?

React is a JavaScript library for building user interfaces. It allows developers to create reusable UI components that can update efficiently and reflect changes in data. React uses a virtual DOM to efficiently update only the necessary parts of the UI, providing a smooth and performant user experience.

What is Spoonacular API?

The Spoonacular API is a powerful API that provides access to a vast collection of recipes and cooking information. It allows developers to search for recipes, retrieve recipe details, and perform various other actions related to cooking and nutrition.

Why build a Recipe App?

Building a Recipe App is a great way to practice your React skills and learn how to integrate external APIs into your applications. It also provides a useful and practical application for users who are looking for recipe inspiration and cooking tips.

Setting up the Project

Creating a new React project

To start building our Recipe App, we first need to create a new React project. We can do this using the Create React App command-line tool. Open your terminal and run the following command:

npx create-react-app recipe-app

This will create a new directory called "recipe-app" with all the necessary files and dependencies to get started with React.

Installing necessary dependencies

Next, we need to install the necessary dependencies for our Recipe App. Open your terminal, navigate to the "recipe-app" directory, and run the following command:

npm install axios react-router-dom

This will install the Axios library, which we will use to make API requests, and the React Router DOM library, which we will use for routing within our application.

Setting up API credentials

Before we can start making API requests to the Spoonacular API, we need to obtain API credentials. Visit the Spoonacular API website and sign up for an account. Once you have an account, you can generate API credentials, including an API key. Keep the API key handy as we will need it later to authenticate our requests.

Building the User Interface

Creating components

In our Recipe App, we will create several components to build the user interface. We can start by creating a "Header" component, which will display the app's title and navigation links. Create a new file called "Header.js" in the "src" directory, and add the following code:

import React from "react";
import { Link } from "react-router-dom";

const Header = () => {
  return (
    <header>
      <h1>Recipe App</h1>
      <nav>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/favorites">Favorites</Link>
          </li>
        </ul>
      </nav>
    </header>
  );
};

export default Header;

In this code, we import the necessary React and React Router DOM libraries. We define a functional component called "Header" that returns the HTML markup for the header section of our app. The header includes the app's title and navigation links to the home page and the favorites page.

Designing the layout

Next, we will design the layout for our Recipe App. Create a new file called "App.js" in the "src" directory, and add the following code:

import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Header from "./Header";

const App = () => {
  return (
    <Router>
      <div className="App">
        <Header />
        <main>
          <Switch>
            {/* Routes will be added here */}
          </Switch>
        </main>
      </div>
    </Router>
  );
};

export default App;

In this code, we import the necessary React Router DOM components. We define a functional component called "App" that wraps our entire application with the Router component. The Router component enables client-side routing in our app. Inside the App component, we include the Header component and a main section where we will add our app's routes.

Implementing search functionality

To allow users to search for recipes, we will create a "SearchForm" component. Create a new file called "SearchForm.js" in the "src" directory, and add the following code:

import React, { useState } from "react";

const SearchForm = () => {
  const [query, setQuery] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    // Perform search with query
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Search for recipes..."
        value={query}
        onChange={(e) => setQuery(e.target.value)}
      />
      <button type="submit">Search</button>
    </form>
  );
};

export default SearchForm;

In this code, we import the necessary React library and the useState hook. We define a functional component called "SearchForm" that manages the state of the search query using the useState hook. The component renders a form with an input field and a submit button. When the form is submitted, the handleSubmit function is called, preventing the default form submission behavior and performing the search with the query.

Fetching Recipe Data

Making API requests

To fetch recipe data from the Spoonacular API, we will create a "RecipeList" component. Create a new file called "RecipeList.js" in the "src" directory, and add the following code:

import React, { useEffect, useState } from "react";
import axios from "axios";

const RecipeList = () => {
  const [recipes, setRecipes] = useState([]);

  useEffect(() => {
    const fetchRecipes = async () => {
      try {
        const response = await axios.get(
          `https://api.spoonacular.com/recipes/search?apiKey=YOUR_API_KEY&query=chicken`
        );
        setRecipes(response.data.results);
      } catch (error) {
        console.error(error);
      }
    };

    fetchRecipes();
  }, []);

  return (
    <div>
      {recipes.map((recipe) => (
        <div key={recipe.id}>
          <h2>{recipe.title}</h2>
          <img src={recipe.image} alt={recipe.title} />
        </div>
      ))}
    </div>
  );
};

export default RecipeList;

In this code, we import the necessary React library, the useEffect and useState hooks, and the Axios library. We define a functional component called "RecipeList" that manages the state of the recipes using the useState hook. The component uses the useEffect hook to fetch the recipes from the Spoonacular API when the component mounts. The fetched recipes are stored in the state using the setRecipes function. Finally, we render the list of recipes by mapping over the recipes array and displaying the recipe title and image.

Handling response data

To display the recipe details, we will create a "RecipeDetail" component. Create a new file called "RecipeDetail.js" in the "src" directory, and add the following code:

import React, { useEffect, useState } from "react";
import axios from "axios";

const RecipeDetail = ({ match }) => {
  const [recipe, setRecipe] = useState(null);

  useEffect(() => {
    const fetchRecipe = async () => {
      try {
        const response = await axios.get(
          `https://api.spoonacular.com/recipes/${match.params.id}/information?apiKey=YOUR_API_KEY`
        );
        setRecipe(response.data);
      } catch (error) {
        console.error(error);
      }
    };

    fetchRecipe();
  }, [match.params.id]);

  if (!recipe) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h2>{recipe.title}</h2>
      <img src={recipe.image} alt={recipe.title} />
      <p>{recipe.instructions}</p>
    </div>
  );
};

export default RecipeDetail;

In this code, we import the necessary React library, the useEffect and useState hooks, and the Axios library. We define a functional component called "RecipeDetail" that receives the "match" object as a prop from React Router DOM. The "match" object contains information about the current route, including the URL parameters. The component uses the useEffect hook to fetch the recipe details from the Spoonacular API based on the recipe ID in the URL parameters. The fetched recipe is stored in the state using the setRecipe function. If the recipe is not available yet, we display a loading message. Once the recipe is available, we render the recipe title, image, and instructions.

Adding Additional Features

Implementing favorites functionality

To allow users to add recipes to their favorites, we will create a "Favorites" component. Create a new file called "Favorites.js" in the "src" directory, and add the following code:

import React, { useState } from "react";

const Favorites = () => {
  const [favorites, setFavorites] = useState([]);

  const handleAddFavorite = (recipe) => {
    setFavorites((prevFavorites) => [...prevFavorites, recipe]);
  };

  return (
    <div>
      <h2>Favorites</h2>
      {favorites.length === 0 ? (
        <p>No favorites added yet.</p>
      ) : (
        <ul>
          {favorites.map((favorite) => (
            <li key={favorite.id}>
              {favorite.title} <button>Remove</button>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

export default Favorites;

In this code, we import the necessary React library and the useState hook. We define a functional component called "Favorites" that manages the state of the favorites using the useState hook. The component renders a list of favorites. If no favorites are added yet, a message is displayed. Otherwise, the favorites are rendered as list items. Each favorite item includes a remove button, which will be implemented later.

Adding recipe filtering options

To allow users to filter recipes based on specific criteria, we will create a "FilterOptions" component. Create a new file called "FilterOptions.js" in the "src" directory, and add the following code:

import React, { useState } from "react";

const FilterOptions = () => {
  const [cuisine, setCuisine] = useState("");
  const [diet, setDiet] = useState("");

  const handleCuisineChange = (e) => {
    setCuisine(e.target.value);
  };

  const handleDietChange = (e) => {
    setDiet(e.target.value);
  };

  return (
    <div>
      <h2>Filter Options</h2>
      <label>
        Cuisine:
        <select value={cuisine} onChange={handleCuisineChange}>
          <option value="">Any</option>
          <option value="italian">Italian</option>
          <option value="mexican">Mexican</option>
          <option value="indian">Indian</option>
        </select>
      </label>
      <label>
        Diet:
        <select value={diet} onChange={handleDietChange}>
          <option value="">Any</option>
          <option value="vegetarian">Vegetarian</option>
          <option value="vegan">Vegan</option>
          <option value="gluten free">Gluten Free</option>
        </select>
      </label>
    </div>
  );
};

export default FilterOptions;

In this code, we import the necessary React library and the useState hook. We define a functional component called "FilterOptions" that manages the state of the cuisine and diet options using the useState hook. The component renders two dropdown menus for selecting the cuisine and diet options. The selected values are stored in the state and can be accessed using the "cuisine" and "diet" variables.

Including social sharing capabilities

To allow users to share recipes on social media, we will create a "SocialShare" component. Create a new file called "SocialShare.js" in the "src" directory, and add the following code:

import React from "react";

const SocialShare = () => {
  const handleShare = () => {
    // Implement share functionality
  };

  return (
    <div>
      <h2>Social Share</h2>
      <button onClick={handleShare}>Share</button>
    </div>
  );
};

export default SocialShare;

In this code, we import the necessary React library. We define a functional component called "SocialShare" that renders a button for sharing the recipe on social media. The handleShare function will be implemented later to handle the actual sharing functionality.

Testing and Deployment

Writing unit tests

To ensure the reliability of our Recipe App, we will write unit tests for our components. Create a new file called "App.test.js" in the "src" directory, and add the following code:

import React from "react";
import { render, screen } from "@testing-library/react";
import App from "./App";

test("renders header", () => {
  render(<App />);
  const headerElement = screen.getByText(/Recipe App/i);
  expect(headerElement).toBeInTheDocument();
});

In this code, we import the necessary React library and the render and screen functions from the "@testing-library/react" library. We define a unit test that checks if the header of our App component is rendered correctly. The screen.getByText function is used to find the header element with the text "Recipe App". The expect function is used to assert that the header element is in the document.

Running test suites

To run our unit tests, open your terminal, navigate to the "recipe-app" directory, and run the following command:

npm test

This will launch the test runner and execute our unit tests. The test runner will provide feedback on the success or failure of each test.

Deploying the app

To deploy our Recipe App, we can use platforms like Netlify or Vercel. Here, we will use Netlify for simplicity. Follow these steps to deploy the app:

  1. Sign up for a Netlify account if you don't have one already.
  2. In your terminal, navigate to the "recipe-app" directory.
  3. Run the following command to create a production build of your app:
npm run build

This will create a "build" directory with optimized and minified versions of your app's files.

  1. Run the following command to deploy your app to Netlify:
npx netlify deploy

Follow the prompts in the terminal to authenticate with Netlify and select the deployment options.

  1. Once the deployment is complete, Netlify will provide you with a URL where your app is accessible.

Congratulations! You have successfully deployed your Recipe App.

Conclusion

In this tutorial, we learned how to build a Recipe App using React and the Spoonacular API. We started by setting up the project, including creating a new React project and installing necessary dependencies. Then, we built the user interface by creating components and implementing search functionality. We also learned how to fetch recipe data from the Spoonacular API and display recipe details. Additionally, we added additional features like favorites functionality, recipe filtering options, and social sharing capabilities. Finally, we covered testing our components and deploying the app to Netlify.

Building a Recipe App with React and Spoonacular API is a great way to enhance your React skills and gain hands-on experience with integrating external APIs. You can further expand the app's functionality by adding user authentication, rating and review system, and more. Happy coding!