DEV Community

Cover image for ๐Ÿณ From Fridge to Fork: Building a Smart Recipe Finder with React & Spoonacular API
Allan Niรฑal
Allan Niรฑal

Posted on

๐Ÿณ From Fridge to Fork: Building a Smart Recipe Finder with React & Spoonacular API

Ever stood in front of your fridge, staring at random ingredients, wondering "What the heck can I make with leftover chicken, some bell peppers, and that lonely onion?" Well, I built an app for exactly that!

๐ŸŽฏ The Problem: Fridge Tetris & Recipe Roulette

We've all been there - you're hungry, you open the fridge, and it's like playing ingredient Tetris. You've got:

  • Half a bell pepper (slightly wrinkled but still good!)
  • Some chicken breast that expires tomorrow
  • Random herbs you bought for that one recipe two weeks ago
  • A can of tomatoes you forgot about

Instead of ordering takeout (again), I decided to build a smart Recipe Finder app that turns your random ingredients into actual meal suggestions!

๐Ÿš€ What We're Building

The Recipe Finder is a React-powered web app that:

  • โœจ Takes your ingredients as input
  • ๐Ÿ” Searches thousands of recipes via Spoonacular API
  • ๐Ÿ“ฑ Shows beautiful recipe cards with images
  • ๐ŸŽฏ Matches recipes based on what you actually have

Here's how the magic happens:

Image description

๐Ÿ—๏ธ Tech Stack & Architecture

Before diving into code, let's look at our simple but effective tech stack:

{
  "frontend": "React 18 + Vite",
  "styling": "Vanilla CSS with inline styles",
  "api": "Spoonacular Recipe API",
  "http": "Axios for API calls",
  "dev_tools": "ESLint + Vite dev server"
}
Enter fullscreen mode Exit fullscreen mode

Image description

๐Ÿ”ง Setting Up the Foundation

Let's start with the project structure. I used Vite because it's blazingly fast and perfect for React projects:

npm create vite@latest recipe-finder -- --template react
cd recipe-finder
npm install axios
Enter fullscreen mode Exit fullscreen mode

The beauty of this setup? You get hot reload, optimized builds, and a development server that starts in milliseconds!

๐Ÿณ The Core Recipe Logic

Here's where the magic happens. The main App.jsx component handles all our recipe-finding logic:

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

// API Configuration - keep your secrets safe!
const API_KEY = import.meta.env.VITE_SPOONACULAR_API_KEY;
const BASE_URL = "https://api.spoonacular.com/recipes/findByIngredients";

function App() {
    const [ingredientList, setIngredientList] = useState("");
    const [recipeList, setRecipeList] = useState([]);
    const [errorMessage, setErrorMessage] = useState("");

    const fetchRecipes = async () => {
        if (!ingredientList.trim()) {
            setErrorMessage("Please enter some ingredients.");
            return;
        }

        setErrorMessage("");
        setRecipeList([]);

        try {
            const { data } = await axios.get(BASE_URL, {
                params: {
                    ingredients: ingredientList,
                    number: 5,
                    apiKey: API_KEY,
                },
            });
            setRecipeList(data || []);
        } catch (error) {
            console.error("Error fetching recipes:", error);
            setErrorMessage("Failed to fetch recipes. Please try again.");
        }
    };

    return (
        <div style={{ padding: "20px", maxWidth: "600px", margin: "0 auto" }}>
            <h1>Recipe Finder</h1>
            <textarea
                rows="5"
                value={ingredientList}
                onChange={(e) => setIngredientList(e.target.value)}
                placeholder="Enter your ingredients (comma-separated)..."
                style={{ width: "100%", marginBottom: "10px", padding: "10px" }}
            />
            <button onClick={fetchRecipes}>Find Recipes</button>
            {errorMessage && <p style={{ color: "red" }}>{errorMessage}</p>}
            <RecipeList recipes={recipeList} />
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

๐ŸŽจ Creating Beautiful Recipe Cards

The recipe display component is where the app really shines. Each recipe gets its own card with an image and title:

function RecipeList({ recipes }) {
    if (recipes.length === 0) return null;

    return (
        <div style={{ marginTop: "20px" }}>
            <h3>Suggested Recipes:</h3>
            <div style={{ 
                display: "flex", 
                flexWrap: "wrap", 
                gap: "20px",
                listStyle: "none",
                padding: 0 
            }}>
                {recipes.map((recipe, index) => (
                    <div key={index} style={{
                        textAlign: "center",
                        width: "200px",
                        padding: "10px",
                        border: "1px solid #ddd",
                        borderRadius: "8px",
                        boxShadow: "0 2px 4px rgba(0,0,0,0.1)"
                    }}>
                        <img 
                            src={recipe.image} 
                            alt={recipe.title} 
                            style={{
                                width: "100%",
                                height: "150px",
                                objectFit: "cover",
                                borderRadius: "4px",
                                marginBottom: "8px"
                            }} 
                        />
                        <strong>{recipe.title}</strong>
                    </div>
                ))}
            </div>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

Image description

๐Ÿ” Environment Variables & Security

Security first! Never expose your API keys. I use Vite's environment variable system:

# .env file
VITE_SPOONACULAR_API_KEY=your_actual_api_key_here
Enter fullscreen mode Exit fullscreen mode
// In your app
const API_KEY = import.meta.env.VITE_SPOONACULAR_API_KEY;
Enter fullscreen mode Exit fullscreen mode

Pro tip: Add .env to your .gitignore file! Nothing ruins your day like accidentally pushing API keys to GitHub ๐Ÿ˜…

๐ŸŒŸ The Spoonacular API Magic

The Spoonacular API is the secret sauce here. Their findByIngredients endpoint is perfect for our use case:

const apiResponse = await axios.get(BASE_URL, {
    params: {
        ingredients: "chicken,tomatoes,onions", // comma-separated
        number: 5, // limit results
        apiKey: API_KEY,
    },
});
Enter fullscreen mode Exit fullscreen mode

The API returns an array of recipes with:

  • ๐Ÿ“ท Beautiful food images
  • ๐Ÿท๏ธ Recipe titles
  • ๐Ÿฅ• Which ingredients were used
  • ๐Ÿ“Š How many ingredients were missing

Image description

๐Ÿš€ Running the App

Getting this beauty running is super simple:

# Install dependencies
npm install

# Start the dev server
npm run dev

# Build for production
npm run build
Enter fullscreen mode Exit fullscreen mode

Then open your browser to http://localhost:5173 and start discovering recipes!

๐Ÿ’ก Cool Features & Future Enhancements

What makes this app special:

โœจ Current Features:

  • Instant Search - Type ingredients, get recipes immediately
  • Visual Results - Beautiful recipe cards with images
  • Error Handling - Graceful failures with helpful messages
  • Responsive Design - Works on desktop and mobile

๐Ÿ”ฎ Future Improvements:

  • ๐Ÿฅ— Dietary filters (vegetarian, vegan, gluten-free)
  • โญ Recipe ratings and reviews
  • ๐Ÿ›’ Shopping list generation for missing ingredients
  • ๐Ÿ“ฑ Mobile app version
  • ๐Ÿฝ๏ธ Meal planning features

๐Ÿ“Š Performance & Best Practices

Here are some optimization techniques I used:

// Efficient state management
const [ingredientList, setIngredientList] = useState("");
const [recipeList, setRecipeList] = useState([]);
const [errorMessage, setErrorMessage] = useState("");

// Input validation before API calls
if (!ingredientList.trim()) {
    setErrorMessage("Please enter some ingredients.");
    return;
}

// Error handling with user-friendly messages
catch (error) {
    console.error("Error fetching recipes:", error);
    setErrorMessage("Failed to fetch recipes. Please try again.");
}
Enter fullscreen mode Exit fullscreen mode

๐ŸŽฏ Key Learnings & Gotchas

Building this app taught me several valuable lessons:

  1. API Rate Limits - Spoonacular has daily limits, so implement caching for production
  2. Error Handling - Always assume the API might fail and handle it gracefully
  3. UX Matters - Loading states and error messages make users happy
  4. Keep It Simple - Sometimes vanilla CSS is better than heavy frameworks

๐Ÿ”— Try It Yourself!

Want to build your own Recipe Finder? Here's how:

  1. Get a Spoonacular API key (they have a free tier!)
  2. Clone the project: Recipe Finder on GitHub
  3. Add your API key to the .env file
  4. Start coding and make it your own!

๐ŸŒŸ More AI-Powered Projects

This Recipe Finder is part of my Mini AI Projects collection, where I explore different APIs and AI services. Check out my other projects:

  • ๐Ÿ–ผ๏ธ Image Captioning App - AI describes your photos
  • ๐Ÿ’ฌ Sentiment Analysis Tool - Analyze text emotions
  • ๐Ÿค– Personal AI Chatbot - Your own DialoGPT assistant
  • ๐Ÿ“„ Document Summarizer - BART-powered text summarization

๐ŸŽ‰ Conclusion

Building the Recipe Finder was an absolute blast! It solves a real problem (what to cook with random ingredients) while being simple enough to understand and extend.

The combination of React's reactivity, Vite's speed, and Spoonacular's rich API creates a powerful yet lightweight application. Whether you're a beginner learning React or an experienced developer looking for a fun weekend project, this recipe finder is a perfect starting point.

What's your favorite coding project that solves an everyday problem? Drop a comment below! ๐Ÿ‘‡


Want to build your own AI-powered web app? I'd love to help! Reach out to me at landix.ninal@gmail.com and let's create something amazing together! ๐Ÿš€


Find the complete source code on GitHub and follow me for more AI/ML projects!

Top comments (0)