A modern web application for AI-powered recipe generation, built with React, TypeScript, and Tailwind CSS.
- 🎨 Clean, modern UI with Tailwind CSS
- 📝 Recipe generation form with advanced options
- 🎯 Nutrition targeting (calories, protein, carbs, fat) per serving
- 🥕 Ingredient availability specification
- 📖 Beautiful recipe display with step-by-step instructions
- 🖼️ AI-generated recipe images
- 📊 Automatic per-serving nutrition calculation
- React 18 - UI framework
- TypeScript - Type safety
- Vite - Build tool and dev server
- Tailwind CSS - Utility-first styling
- Fetch API - Backend communication via REST
Run the entire stack from the project root:
docker-compose up --buildThe frontend will be available at http://localhost:3000
Run the frontend locally while the backend runs in Docker:
# From project root
docker-compose up backend# From frontend directory
npm installnpm run devThe application will be available at http://localhost:3000
frontend/
├── src/
│ ├── App.tsx # Main application component
│ ├── RecipeForm.tsx # Recipe generation form with inputs
│ ├── RecipeDisplay.tsx # Recipe display component
│ ├── grpcClient.ts # API client (REST, not gRPC anymore)
│ ├── types.ts # TypeScript type definitions
│ ├── main.tsx # Application entry point
│ └── index.css # Global styles with Tailwind
├── public/ # Static assets
├── vite.config.ts # Vite configuration
├── tailwind.config.js # Tailwind CSS configuration
├── tsconfig.json # TypeScript configuration
├── package.json # Dependencies and scripts
└── README.md # This file
npm run dev # Start development server (http://localhost:3000)
npm run build # Build for production
npm run preview # Preview production build-
User Input: User fills out the recipe generation form
- Recipe description (required)
- Complexity level (optional: easy, medium, hard)
- Target nutrition per serving (optional)
- Available ingredients (optional)
-
API Request: Form data is sent to the FastAPI backend
- Endpoint:
POST http://localhost:8000/api/recipes/generate - Format: JSON with snake_case fields
- Endpoint:
-
Backend Processing: The backend's AI agent generates the recipe
- Searches for recipe inspiration
- Calculates nutrition for entire recipe
- Generates an AI image
-
Response Handling: Frontend receives and transforms the data
- Converts snake_case to camelCase
- Calculates per-serving nutrition (divides by servings)
- Displays the recipe with image
-
Recipe Display: Beautiful presentation of the recipe
- Recipe title and description
- AI-generated image
- Prep/cook times and servings
- Ingredients list
- Nutrition facts (per serving)
- Step-by-step instructions
- Citations/sources
App.tsx
├── RecipeForm (onSubmit → generateRecipe)
│ ├── Basic inputs (description, complexity)
│ └── Advanced options
│ ├── Target nutrition inputs
│ └── Available ingredients manager
└── RecipeDisplay (recipe, onBack)
├── Recipe header (title, description)
├── Recipe image
├── Meta info (times, servings)
├── Two-column layout
│ ├── Ingredients list
│ └── Nutrition facts (calculated per serving)
└── Instructions by section
The grpcClient.ts file handles all communication with the backend:
Request Format:
{
description: string; // Required
complexity?: string; // Optional: "easy" | "medium" | "hard"
target_macros?: { // Optional, per serving
calories?: number;
protein_grams?: number;
carbs_grams?: number;
fat_grams?: number;
};
available_ingredients?: Array<{
name: string;
quantity: number;
unit: string;
}>;
}Response Format:
{
recipe_id: string;
title: string;
description: string;
ingredients: Array<Ingredient>;
instructions: Array<InstructionSection>;
prep_time_minutes: number;
cook_time_minutes: number;
nutrition: NutritionProfile; // Total for entire recipe
servings: number;
serving_size?: string;
citations?: string[];
image_base64?: string; // Base64 PNG image
}The backend returns nutrition for the entire recipe. The frontend calculates per-serving values:
// Display per-serving nutrition
const perServingCalories = Math.round(recipe.nutrition.calories / recipe.servings);All data models are defined in types.ts:
Recipe- Complete recipe with all fieldsIngredient- Individual ingredientNutritionProfile- Nutritional informationInstructionSection- Grouped cooking stepsGenerateRecipeRequest- API request payload
The app uses Tailwind CSS with a custom configuration:
Color Scheme:
- Primary: Blue (
blue-600,blue-700) - Background: White and light gray
- Text: Gray scale for hierarchy
Responsive Design:
- Mobile-first approach
- Grid layouts for ingredient/nutrition display
- Responsive navigation and forms
Simple React state using useState:
recipe- Currently displayed recipeloading- Request in progresserror- Error message if any- Form state for all inputs
Problem: "Failed to fetch" or connection errors
Solutions:
- Verify backend is running:
curl http://localhost:8000/
- Check Docker network if using containers
- Review browser console for CORS errors
- Ensure backend is on port 8000
Problem: Form submits but no recipe appears
Solutions:
- Check browser console for errors
- Verify backend logs:
docker-compose logs -f backend
- Test API directly:
curl -X POST http://localhost:8000/api/recipes/generate \ -H "Content-Type: application/json" \ -d '{"description": "pasta with chicken"}'
Problem: Recipe displays but no image
Solutions:
- Check if
imageBase64is in the response - Verify the base64 string is valid
- Check browser console for image load errors
- Backend may have failed to generate image (check logs)
Problem: npm run build fails
Solutions:
- Delete
node_modulesand reinstall:rm -rf node_modules package-lock.json npm install
- Check TypeScript errors:
npm run build 2>&1 | grep "error TS"
- Ensure all types are correctly defined
Build for production:
npm run buildThe optimized files will be in the dist/ directory.
- Static Hosting: Deploy to Vercel, Netlify, or AWS S3
- Docker: Use the production container
- CDN: Serve from any CDN with static file support
Environment Configuration:
For production, update the API endpoint in grpcClient.ts:
const API_ENDPOINT = process.env.VITE_API_URL || 'http://localhost:8000/api/recipes/generate';Then set the environment variable during build:
VITE_API_URL=https://your-api.com npm run build- Main README - Project overview
- Backend README - Backend development
- QUICKSTART - Quick start guide
- API Docs - Interactive API documentation (when running)
ISC