- Introduction
- Main features
- Project Structure
- Routing
- API Integration and Asynchronous Communication in Growl
- Figma
- Deployment 8Deployment 9Final conclusion
This project is a Web App developed using React + Vite. It offers features such as recipe searching,
bookmarking and filtering by ingredients, colories and diet type.
- Register and login.
- Profile editing.
- Contact form.
- Allows to search and filter amongst lots of recipes thanks to Edamam API, as well as bookmarking them.
- Specific page for each recipe to display all its data such as ingredients, allergens, diet to which it could belong etc.
- Main profile page where we can see our bokkmarked recipes and deleted them from bookmarks as well.
- Edit profile page where we can edit some profile settings such as our name, profile picture and recipes tags.
- Custom 404 page with an animation.
The project is organized in the following folders
src/: Our project folder.assets/: Contains images and static resourcescss/: The folder where all css styles can be found.font/: The folder where all external fonts used can be found.components/: Contains reusable components, in this project and to organise it better, apart from the components folder filled with components used on different pages, I've added an specific components folder on each page folder.helpers/: JS functions that, as its name tells, help us doing specific task in our app.layouts/: General private and public layout folderLayoutPublic/: Our public Layout folder.LayoutPrivatec/: Our private Layout folder.
pages/: Our app main pages folder.router/: App routes config.
This app uses react-router-dom to handle routes, the following routing is the one I've implemented:
Root Route (/):
It displays the public layout and the home page (Home)
User related routes:
/login: SignUp page/sign-inSign in page/contact-usContact form page/profileProfile page/profile/editProfile edit page
Search route(/search):
/searchOur recipe searching page./search/recipe/:foodIdSpecific recipe page
Errors:
Custom 404 error page when the route isn't found
This project utilizes the Edamam public API to fetch real-time data about food recipes. The integration and asynchronous
communication are implemented in the SearchPage component, leveraging React hooks (useState, useEffect) to
manage local state and handle the lifecycle of API requests.
The SearchPage component is the only part of the app that interacts directly with the API. Its primary purpose is to allow users to search for recipes and navigate through the results pages.
The fetch method is used for asynchronous API requests. The data received is processed and dynamically updated in the user interface.
Example Request:
const fetchData = async (url) => {
try {
setLoading(true)
const response = await fetch(url)
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
const data = await response.json()
setResults(data.hits.map(hit => hit.recipe))
setNextPage(data._links?.next?.href || null)
} catch (error) {
setError(error.message)
} finally {
setLoading(false)
}
}results: Holds the recipes fetched from the API.loading: Tracks whether data is being loaded.error: Stores any errors that occur during API requests.nextPage: URL for loading the next page of results.prevPages: A history of previously visited pages to enable backward navigation.
The Edamam API supports pagination, and the SearchPage component implements this feature using the following controls:
- "Next Page" Button: Fetches the next page using the URL stored in nextPage.
- "Previous Page" Button: Navigates back to the previous page using the prevPages history.
Navigation Handling Example:
const loadNextPage = () => {
if (nextPage) fetchData(nextPage)
}
const loadPreviousPage = () => {
if (prevPages.length > 1) {
const previousPage = prevPages[prevPages.length - 2]
setPrevPages(prev => prev.slice(0, -1))
fetchData(previousPage, false)
}
}The data fetched from the API is dynamically rendered using the FoodCard component. This component displays relevant information, such as:
- Recipe name.
- Image.
- Calories.
Conditional Rendering Example:
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error}</div>
return (
<div>
{results.length > 0 ? (
results.map((food, index) => <FoodCard key={index} food={food} />)
) : (
<div>No results found.</div>
)}
</div>
)- While data is being fetched, a "Loading..." message is displayed.
- If an error occurs (e.g., network issues or a non-successful HTTP status), an error message is shown to inform the user.
- An initial request is made to the API with default parameters (e.g., recipes for a high-protein diet).
- The results are processed and displayed as recipe cards.
Initial Request Example:
useEffect(() => {
const query = ""
const diet = "high-protein"
const appId = import.meta.env.VITE_EDAMAM_APP_ID
const appKey = import.meta.env.VITE_EDAMAM_APP_KEY
const initialUrl = `https://api.edamam.com/api/recipes/v2?type=public&q=${query}&app_id=${appId}&app_key=${appKey}&diet=${diet}`
fetchData(initialUrl)
}, [])Link to initial design on figma: Growl
App deployed on netlify: Growl
Video presentation: Video
With this project I consider I've improved my skills on React, I've faced some problems with the API like trying to paginate it due to how the API is made, it got successfully fixed just by, I also got some problems with Formik because in a first stance, I didn't understand it correctly, after reading and seeing examples online I got no further problem, finally I also experienced problems while leraning how REACT handles css styles, I managed to style my page while investigating it, anyways, I recognise it's hasn't the better practises on a design matter so it'll be improved in future projects.

