Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Miguelcds/Recipe-Hub/llms.txt

Use this file to discover all available pages before exploring further.

All components live in src/components/. They are presentational by default — they receive props and render UI. The only exception is RecipeDetailCard, which reads from FavoritesContext directly.

NavBar

Site-wide navigation links rendered in the app header.

Footer

Static copyright footer rendered on every page.

SearchBar

Controlled search input with button click handler and validation.

RecipeCard

Linked thumbnail card for a single recipe in a list.

RecipeList

Grid container that maps an array of recipes to RecipeCard elements.

RecipeDetailCard

Full recipe view with ingredients, instructions, video, and favorite toggle.

Renders the primary navigation using NavLink from React Router. Active links receive the active class automatically. Props: none
src/components/NavBar.jsx
import React from 'react'
import { NavLink } from 'react-router-dom'
import './NavBar.css'

export const NavBar = () => {
  return (
    <nav>
      <ul id="nav-principal">
        <li>
          <NavLink className={({ isActive }) => isActive ? "active" : ""} to="favorites">
            Favorites💞
          </NavLink>
        </li>
        <li><NavLink to="/">Home 🏠</NavLink></li>
        <li><NavLink to="contact">Contact 📡</NavLink></li>
      </ul>
    </nav>
  )
}
NavBar is a named export (export const NavBar). Import it as import { NavBar } from '../components/NavBar'. The active class is applied manually via the className callback on the Favorites link.

Static footer with copyright text. Powered by TheMealDB attribution. Props: none
src/components/Footer.jsx
import './Footer.css'

const Footer = () => {
  return (
    <footer className="footer">
      <p>© 2025 The Secret Recipe. Powered by TheMealDB.</p>
    </footer>
  )
}

export default Footer

Controlled search input. The parent (Home) owns the search state string and passes it down alongside a click handler. Empty-string validation happens in Home.handleClick before calling fetchRecipes.

Props

PropTypeDescription
searchstringControlled input value, owned by the parent
setSearch(value: string) => voidUpdates the search string in parent state on every keystroke
onSearch() => voidCalled when the Buscar button is clicked
errorstringValidation error message to display below the input
src/components/SearchBar.jsx
import "./SearchBar.css"

export const SearchBar = ({ search, setSearch, onSearch, error }) => {
  return (
    <div className="search-superior">
      <div className="search-container">
        <input
          id="search-input"
          type="text"
          placeholder="Search Recipes..."
          value={search}
          onChange={(e) => setSearch(e.target.value)}
        />
        <button onClick={onSearch}>Buscar</button>
      </div>
      {error && <p>{error}</p>}
    </div>
  )
}
SearchBar is a named export (export const SearchBar). Import it as import { SearchBar } from '../components/SearchBar'. The component renders any non-empty error string below the input field.

RecipeCard

Rendered by RecipeList for each item in a recipe array. Wraps the entire card in a Link to the recipe detail page.

Props

PropTypeDescription
idstringMeal ID from TheMealDB; used to build the /recipe/:id link
namestringRecipe name shown below the image
picturestringThumbnail image URL
categorystringMeal category label
src/components/RecipeCard.jsx
import { Link } from 'react-router-dom'
import './RecipeCard.css'

const RecipeCard = ({ id, name, picture, category }) => {
  return (
    <Link to={`/recipe/${id}`} className="recipe-card">
      <img src={picture} alt={name} />
      <div className="recipe-info">
        <h3>{name}</h3>
        <span className="category">{category}</span>
      </div>
    </Link>
  )
}

export default RecipeCard

RecipeList

Receives an array of recipe objects and renders a grid of RecipeCard elements. Returns null when the array is empty, so the page renders nothing (rather than an empty container) before a search is performed.

Props

PropTypeDescription
recipesArray<{ id, name, picture, category }>Array of normalized recipe objects from the API service
src/components/RecipeList.jsx
import RecipeCard from './RecipeCard'
import './RecipeList.css'

const RecipeList = ({ recipes }) => {
  if (!recipes.length) return null

  return (
    <div className="recipe-list">
      {recipes.map((recipe) => (
        <RecipeCard
          key={recipe.id}
          id={recipe.id}
          name={recipe.name}
          picture={recipe.picture}
          category={recipe.category}
        />
      ))}
    </div>
  )
}

export default RecipeList

RecipeDetailCard

Displays the full detail view of a recipe. This is the only component that reads from context directly — it calls useFavorites to get toggleFavorite and isFavorite, enabling the favorite button without prop drilling.

Props

PropTypeDescription
recipeobjectFull recipe object (see shape below)
recipe object shape:
FieldTypeDescription
idstringMeal ID
namestringRecipe name
picturestringImage URL
categorystringMeal category
instructionsstringFull cooking instructions
videostring | nullYouTube URL, or null if unavailable
ingredientsArray<{ name: string, measure: string }>List of ingredients with measures
src/components/RecipeDetailCard.jsx
import ReactPlayer from "react-player"
import { useFavorites } from "../context/FavoritesContext"
import "./RecipeDetailCard.css"

const RecipeDetailCard = ({ recipe }) => {
  const { isFavorite, toggleFavorite } = useFavorites()
  return (
    <div className="recipe-detail">
      <img src={recipe.picture} alt="" />
      <h3>{recipe.name}</h3>
      <p>{recipe.category}</p>
      <h5>Ingredients:</h5>
      <ul style={{ listStyle: "none" }}>
        {recipe.ingredients?.map((ig, i) => (
          <li key={i}>
            {ig.name} - {ig.measure}
          </li>
        ))}
      </ul>
      <ReactPlayer src={recipe.video} controls width="100%" height="400px" />
      <h4>{recipe.instructions}</h4>
      <button onClick={() => toggleFavorite(recipe.id)}>
        {isFavorite(recipe.id) ? "💔 Remove from Favs" : "❤️ Add to Favs"}
      </button>
    </div>
  )
}

export default RecipeDetailCard
The ReactPlayer is always rendered (not conditionally). It receives src (not url) and renders an embedded player. The video value from TheMealDB is the YouTube URL stored in recipe.video.

Build docs developers (and LLMs) love