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.

Recipe Hub uses React’s Context API to share favorites state across the component tree without prop drilling. FavoritesContext stores an array of saved meal IDs and exposes functions to add, remove, and check favorites. The state is persisted to localStorage so it survives page refreshes.

Source

src/context/FavoritesContext.jsx
import { createContext, useState, useContext, useEffect } from "react";

const FavoritesContext = createContext();

export const FavoriteProvider = ({ children }) => {
  const [favorites, setFavorite] = useState(() => {
    const prev = localStorage.getItem("favorites");
    return prev ? JSON.parse(prev) : [];
  });

  useEffect(() => {
    localStorage.setItem("favorites", JSON.stringify(favorites));
  }, [favorites]);

  const toggleFavorite = (id) => {
    setFavorite((prev) => {
      if (prev.includes(id)) {
        return prev.filter((f) => f !== id);
      }
      return [...prev, id];
    });
  };

  const isFavorite = (id) => favorites.includes(id);

  return (
    <FavoritesContext.Provider value={{ favorites, toggleFavorite, isFavorite }}>
      {children}
    </FavoritesContext.Provider>
  );
};

export const useFavorites = () => useContext(FavoritesContext);

Context value interface

ValueTypeDescription
favoritesstring[]Array of favorited meal IDs (e.g. ["52772", "53049"])
toggleFavorite(id: string) => voidAdds id to the array if not present; removes it if already present
isFavorite(id: string) => booleanReturns true if id is in the favorites array

localStorage persistence

Initialization

The useState call uses a lazy initializer — a function rather than a plain value. This runs once on mount and reads the current localStorage value:
const [favorites, setFavorite] = useState(() => {
  const prev = localStorage.getItem("favorites");
  return prev ? JSON.parse(prev) : [];
});
If the key does not exist (first visit), the state initializes to an empty array.

Sync on change

A useEffect watches the favorites array and writes it to localStorage on every change:
useEffect(() => {
  localStorage.setItem("favorites", JSON.stringify(favorites));
}, [favorites]);
This keeps the stored value in sync without any manual save step.

Wrapping the app with FavoriteProvider

FavoriteProvider is applied at the root in src/main.jsx, wrapping BrowserRouter so that context is available to every page and component in the router.
FavoriteProvider must wrap BrowserRouter — not the other way around. Placing the provider inside the router would cause it to unmount and remount on navigation, resetting favorites state.
src/main.jsx
createRoot(document.getElementById("root")).render(
  <StrictMode>
    <FavoriteProvider>
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<App />}>
            <Route index element={<Home />} />
            <Route path="favorites" element={<Favorites />} />
            <Route path="contact" element={<Contact />} />
            <Route path="recipe/:id" element={<RecipeDetail />} />
            <Route path="*" element={<NotFound />} />
          </Route>
        </Routes>
      </BrowserRouter>
    </FavoriteProvider>
  </StrictMode>,
);

Consuming the context

Any component can access favorites state by calling the exported useFavorites hook. In RecipeDetailCard, it powers the favorite toggle button:
src/components/RecipeDetailCard.jsx
import { useFavorites } from '../context/FavoritesContext'

const RecipeDetailCard = ({ recipe }) => {
  const { toggleFavorite, isFavorite } = useFavorites()
  const favorite = isFavorite(recipe.id)

  return (
    <button
      className={`favorite-btn ${favorite ? 'active' : ''}`}
      onClick={() => toggleFavorite(recipe.id)}
    >
      {favorite ? '❤️ Remove from Favorites' : '🤍 Add to Favorites'}
    </button>
  )
}
isFavorite drives the button label and active CSS class; toggleFavorite handles both add and remove in a single call.

Build docs developers (and LLMs) love