Recipe Finder Using Js

Posted on October 13, 2025 by Vishesh Namdev
Python C C++ Javascript Java
Recipe Finder App using JS

In this project, we’ll build a Recipe Finder Web App using HTML, CSS (Bootstrap), and JavaScript. The app lets users search for recipes by name and view details like ingredients, instructions, and a YouTube video. It uses the TheMealDB API to fetch real recipe data dynamically. The search bar allows instant lookups, and results are displayed in clean, responsive Bootstrap cards. By making this project, you’ll learn how to work with APIs, handle user input, and dynamically update web content with JavaScript.

🧩 Project Overview

The main idea of this project is simple:

  • Built a Recipe Finder Web App using HTML, CSS (Bootstrap), and JavaScript.
  • Integrated TheMealDB API to fetch and display real-time recipe data.
  • Added a search bar to find recipes instantly by name.
  • Displayed results in responsive Bootstrap cards with images and categories.
  • Used a modal window to show detailed recipe information, ingredients, and YouTube tutorials.
  • Demonstrates practical use of API integration, DOM manipulation, and Bootstrap UI design.
  • Navbar and Search Input

    The navigation bar is built using Bootstrap and contains a brand title with a search input field. Users can type a recipe name here to search. The form does not have a submit button — instead, pressing the Enter key triggers the search function.

    <nav class="navbar navbar-expand-lg bg-body-tertiary">
        <div class="container-fluid">
            <a class="navbar-brand" href="#">Recipe Finder</a>
            <form id="searchForm" class="d-flex w-100 justify-content-end" role="search">
                <input id="searchInput" class="form-control search-input" type="search" placeholder="Search recipes...">
            </form>
        </div>
    </nav>

    Fetching Recipes from TheMealDB API

    This function takes the user’s search query and sends a request to the TheMealDB API. It then converts the JSON response into usable JavaScript data and calls another function to display the results.

    async function getMeals(query) { 
      if (!query) { 
        message.textContent = "Please type a recipe name."; 
        mealsDiv.innerHTML = ""; 
        return; 
      }
    
    message.textContent = "Loading recipes...";
    const res = await fetch(https://www.themealdb.com/api/json/v1/1/search.php?s=${query});
    const data = await res.json();
    
    if (!data.meals) {
    message.textContent = "No recipes found.";
    mealsDiv.innerHTML = "";
    return;
    }
    
    message.textContent = Showing ${data.meals.length} recipe(s);
    displayMeals(data.meals);
    }

    Displaying Recipe Cards

    The displayMeals() function dynamically creates Bootstrap cards for each recipe returned by the API. Each card shows the meal image, name, category, and area, along with a button to view full details.

    function displayMeals(meals) {
      mealsDiv.innerHTML = meals.map(meal => `
        <div class="col-12 col-md-4">
          <div class="card h-100 shadow-sm">
            <img src="${meal.strMealThumb}" class="card-img-top" alt="${meal.strMeal}">
            <div class="card-body d-flex flex-column">
              <h5 class="card-title">${meal.strMeal}</h5>
              <p class="text-muted small">${meal.strCategory} | ${meal.strArea}</p>
              <button class="btn btn-primary mt-auto" onclick="showDetails(${meal.idMeal})">
                View Recipe
              </button>
            </div>
          </div>
        </div>
      `).join("");
    }

    Showing Recipe Details in Modal

    When a user clicks the “View Recipe” button, the showDetails() function fetches more details for that meal using its unique ID. It displays the ingredients, instructions, and a YouTube video link inside a Bootstrap modal.

    async function showDetails(mealId) { 
        const res = await fetch(`https://www.themealdb.com/api/json/v1/1/lookup.php?i=${mealId}`); 
        const data = await res.json(); 
        const meal = data.meals[0];
    
        let ingredients = "";
        for (let i = 1; i <= 20; i++) {
            const ing = meal[`strIngredient${i}`];
            const measure = meal[`strMeasure${i}`];
            if (ing) ingredients += "<li>" + ing + " - " + measure + "</li>";
        }
    
        modalBody.innerHTML = 
            "<div class='text-center mb-3'>" +
                "<img src='" + meal.strMealThumb + "' class='img-fluid rounded' style='max-height:250px'>" +
            "</div>" +
            "<h5>" + meal.strMeal + "</h5>" +
            "<p><strong>Category:</strong> " + meal.strCategory + "</p>" +
            "<p><strong>Area:</strong> " + meal.strArea + "</p>" +
            "<h6>Ingredients:</h6>" +
            "<ul>" + ingredients + "</ul>" +
            "<h6>Instructions:</h6>" +
            "<p>" + meal.strInstructions + "</p>" +
            (meal.strYoutube 
                ? "<a href='" + meal.strYoutube + "' target='_blank' class='btn btn-danger'>Watch Video</a>" 
                : ""
            );
    
        recipeModal.show();
    }

    Search Input Event Listener

    This event listener waits for the user to press the Enter key inside the search box. When pressed, it calls the getMeals() function with the user’s input to perform a new search.

    searchInput.addEventListener("keydown", (e) => { 
      if (e.key === "Enter") { 
        e.preventDefault(); 
        getMeals(searchInput.value.trim()); 
      } 
    });

    Default Recipe on Page Load

    When the page first loads, this line automatically searches for “Arrabiata” so the user sees an example recipe right away.

    window.addEventListener("load", () => getMeals("Arrabiata"));

    Full Code

    <!doctype html>
    <html lang="en">
    <head>
      <meta charset="utf-8" />
      <meta name="viewport" content="width=device-width,initial-scale=1" />
      <title>Recipe Finder</title>
    
      <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    
      <style>
        body{ 
          background-color: white; 
        }
        .card img{
           height: 200px; object-fit: cover; 
          }
        .search-input{ 
          width: 400px; 
        }
        @media (max-width: 768px){ 
          .search-input { width: 100%; } }
      </style>
    </head>
    <body>
    
      <nav class="navbar navbar-expand-lg bg-body-tertiary">
        <div class="container-fluid">
          <a class="navbar-brand" href="#">Recipe Finder</a>
          <form id="searchForm" class="d-flex w-100 justify-content-end" role="search">
            <input id="searchInput" class="form-control search-input" type="search" placeholder="Search recipes...">
          </form>
        </div>
      </nav>
    
      <div class="container my-4">
        <div id="message" class="text-center text-muted mb-3"></div>
        <div id="meals" class="row g-3"></div>
      </div>
    
      <div class="modal fade" id="recipeModal" tabindex="-1">
        <div class="modal-dialog modal-lg">
          <div class="modal-content">
            <div class="modal-header">
              <h5 class="modal-title">Recipe Details</h5>
              <button class="btn-close" data-bs-dismiss="modal"></button>
            </div>
            <div class="modal-body" id="modalBody"></div>
          </div>
        </div>
      </div>
    
      <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    
      <script>
        const searchInput = document.getElementById("searchInput");
        const mealsDiv = document.getElementById("meals");
        const message = document.getElementById("message");
        const modalBody = document.getElementById("modalBody");
        const recipeModal = new bootstrap.Modal(document.getElementById("recipeModal"));
    
        async function getMeals(query) {
          if (!query) {
            message.textContent = "Please type a recipe name.";
            mealsDiv.innerHTML = "";
            return;
          }
    
          message.textContent = "Loading recipes...";
          const res = await fetch(`https://www.themealdb.com/api/json/v1/1/search.php?s=${query}`);
          const data = await res.json();
    
          if (!data.meals) {
            message.textContent = "No recipes found.";
            mealsDiv.innerHTML = "";
            return;
          }
    
          message.textContent = `Showing ${data.meals.length} recipe(s)`;
          displayMeals(data.meals);
        }
    
        function displayMeals(meals) {
          mealsDiv.innerHTML = meals.map(meal => `
            <div class="col-12 col-md-4">
              <div class="card h-100 shadow-sm">
                <img src="${meal.strMealThumb}" class="card-img-top" alt="${meal.strMeal}">
                <div class="card-body d-flex flex-column">
                  <h5 class="card-title">${meal.strMeal}</h5>
                  <p class="text-muted small">${meal.strCategory} | ${meal.strArea}</p>
                  <button class="btn btn-primary mt-auto" onclick="showDetails(${meal.idMeal})">
                    View Recipe
                  </button>
                </div>
              </div>
            </div>
          `).join("");
        }
    
        async function showDetails(mealId) {
          const res = await fetch(`https://www.themealdb.com/api/json/v1/1/lookup.php?i=${mealId}`);
          const data = await res.json();
          const meal = data.meals[0];
    
          let ingredients = "";
          for (let i = 1; i <= 20; i++) {
            const ing = meal[`strIngredient${i}`];
            const measure = meal[`strMeasure${i}`];
            if (ing) ingredients += `<li>${ing} - ${measure}</li>`;
          }
    
          modalBody.innerHTML = `
            <div class="text-center mb-3">
              <img src="${meal.strMealThumb}" class="img-fluid rounded" style="max-height:250px">
            </div>
            <h5>${meal.strMeal}</h5>
            <p><strong>Category:</strong> ${meal.strCategory}</p>
            <p><strong>Area:</strong> ${meal.strArea}</p>
            <h6>Ingredients:</h6>
            <ul>${ingredients}</ul>
            <h6>Instructions:</h6>
            <p>${meal.strInstructions}</p>
            ${meal.strYoutube ? `<a href="${meal.strYoutube}" target="_blank" class="btn btn-danger"><i class="bi bi-youtube"></i> Watch Video</a>` : ""}
          `;
    
          recipeModal.show();
        }
    
        searchInput.addEventListener("keydown", (e) => {
          if (e.key === "Enter") {
            e.preventDefault();
            getMeals(searchInput.value.trim());
          }
        });
    
      </script>
    </body>
    </html>
    📢Important Note📢

    How did you feel about this post?

    😍 🙂 😐 😕 😡

    Was this helpful?

    👍 👎