Building a Sustainable Living Tips Generator with Next.js, Flask, and Google Gemini AI - Part 2: Frontend Development
Introduction In this second part of our tutorial series, we'll build a beautiful and responsive frontend for our sustainability tips generator using Next.js and React Bootstrap. We'll focus on creating an intuitive user interface with smooth animations and proper error handling. Check out Part 1 Project Overview The frontend of SustainAI Tips features: A clean, modern user interface Responsive design for all screen sizes Beautiful card-based tips display Loading states and error handling Category-based organization with icons Smooth animations and transitions Prerequisites Before starting, ensure you have: Node.js 14 or higher installed The backend server from Part 1 running Basic knowledge of React and TypeScript Familiarity with CSS and animations Project Structure frontend/ ├── pages/ │ └── index.tsx # Main application page ├── styles/ │ └── globals.css # Global styles ├── package.json # Project dependencies └── README.md # Frontend documentation Step 1: Setting Up the Frontend Project Create the Next.js project: npx create-next-app@latest frontend --typescript cd frontend Install required dependencies: npm install react-bootstrap bootstrap axios react-markdown Update package.json with the following scripts: { "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" } } Step 2: Implementing the Main Application Create pages/index.tsx with the following code: /** * SustainAI Tips Frontend * * Main page component for the SustainAI Tips application. * Provides a user interface for generating personalized sustainability tips * based on location and habits. Features a responsive design with * animated cards and category-based organization. * * @author Antony Ngemu * @date March 2025 */ import { useState } from 'react'; import axios from 'axios'; import { Form, Button, Alert, Container, Row, Col, Card, Spinner } from 'react-bootstrap'; import ReactMarkdown from 'react-markdown'; import 'bootstrap/dist/css/bootstrap.min.css'; /** * Interface for category tips organization */ interface CategoryTips { [key: string]: string[]; } /** * Main page component */ const Home = () => { // State management for form inputs and API response const [location, setLocation] = useState(''); const [habits, setHabits] = useState(''); const [tips, setTips] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); /** * Handle form submission and API call to generate tips * @param e - Form submission event */ const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); setError(null); setTips([]); try { // Make API request to backend const response = await axios.post('http://localhost:5000/api/tips', { location, habits, }); setTips(response.data.tips); } catch (err) { setError('Failed to generate tips. Please try again.'); console.error('Error:', err); } finally { setLoading(false); } }; /** * Get the appropriate icon for each category * @param category - Category name * @returns Emoji icon for the category */ const getCategoryIcon = (category: string): string => { const icons: { [key: string]: string } = { 'Quick Wins': '⚡', 'Sustainable Living': '

Introduction
In this second part of our tutorial series, we'll build a beautiful and responsive frontend for our sustainability tips generator using Next.js and React Bootstrap. We'll focus on creating an intuitive user interface with smooth animations and proper error handling.
Check out Part 1
Project Overview
The frontend of SustainAI Tips features:
- A clean, modern user interface
- Responsive design for all screen sizes
- Beautiful card-based tips display
- Loading states and error handling
- Category-based organization with icons
- Smooth animations and transitions
Prerequisites
Before starting, ensure you have:
- Node.js 14 or higher installed
- The backend server from Part 1 running
- Basic knowledge of React and TypeScript
- Familiarity with CSS and animations
Project Structure
frontend/
├── pages/
│ └── index.tsx # Main application page
├── styles/
│ └── globals.css # Global styles
├── package.json # Project dependencies
└── README.md # Frontend documentation
Step 1: Setting Up the Frontend Project
- Create the Next.js project:
npx create-next-app@latest frontend --typescript
cd frontend
- Install required dependencies:
npm install react-bootstrap bootstrap axios react-markdown
- Update
package.json
with the following scripts:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
}
}
Step 2: Implementing the Main Application
Create pages/index.tsx
with the following code:
/**
* SustainAI Tips Frontend
*
* Main page component for the SustainAI Tips application.
* Provides a user interface for generating personalized sustainability tips
* based on location and habits. Features a responsive design with
* animated cards and category-based organization.
*
* @author Antony Ngemu
* @date March 2025
*/
import { useState } from 'react';
import axios from 'axios';
import { Form, Button, Alert, Container, Row, Col, Card, Spinner } from 'react-bootstrap';
import ReactMarkdown from 'react-markdown';
import 'bootstrap/dist/css/bootstrap.min.css';
/**
* Interface for category tips organization
*/
interface CategoryTips {
[key: string]: string[];
}
/**
* Main page component
*/
const Home = () => {
// State management for form inputs and API response
const [location, setLocation] = useState<string>('');
const [habits, setHabits] = useState<string>('');
const [tips, setTips] = useState<string[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
/**
* Handle form submission and API call to generate tips
* @param e - Form submission event
*/
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError(null);
setTips([]);
try {
// Make API request to backend
const response = await axios.post('http://localhost:5000/api/tips', {
location,
habits,
});
setTips(response.data.tips);
} catch (err) {
setError('Failed to generate tips. Please try again.');
console.error('Error:', err);
} finally {
setLoading(false);
}
};
/**
* Get the appropriate icon for each category
* @param category - Category name
* @returns Emoji icon for the category
*/
const getCategoryIcon = (category: string): string => {
const icons: { [key: string]: string } = {
'Quick Wins': '⚡',
'Sustainable Living': '