Skip to content

Session 6 WebApp Frontend

Maximiliano Militzer edited this page Apr 7, 2025 · 1 revision

Session 6: WebApp - Frontend (Next.js)

Overview

Welcome to Session 6! In this session, we'll focus on building the frontend of our web application using Next.js, a powerful React framework. Next.js provides features like server-side rendering, static site generation, and simplified routing that make it an excellent choice for modern web applications.

Learning Objectives

  • Understand what Next.js is and its advantages
  • Set up a Next.js project
  • Learn React fundamentals
  • Create responsive layouts with Tailwind CSS
  • Build reusable components
  • Implement client-side routing
  • Fetch data from APIs

Topics Covered

1. Introduction to Next.js

Next.js is a React framework that provides structure, features, and optimizations for your application.

Key Features:

  • Server-Side Rendering (SSR): Renders pages on the server for better SEO and performance
  • Static Site Generation (SSG): Pre-renders pages at build time for faster load times
  • File-based Routing: Creates routes based on your file structure
  • API Routes: Allows you to build API endpoints as part of your Next.js app
  • Automatic Code Splitting: Loads only the code needed for each page
  • Built-in CSS and Sass Support: Allows importing CSS/Sass files directly
  • Fast Refresh: Preserves component state during development

2. Setting Up a Next.js Project

# Create a new Next.js app
npx create-next-app my-todo-app
cd my-todo-app

# Start the development server
npm run dev

Project Structure:

my-todo-app/
├── node_modules/
├── public/          # Static assets
├── src/
│   ├── app/         # App router pages and layouts
│   │   ├── page.js  # Home page (/route)
│   │   └── layout.js # Root layout
│   ├── components/  # Reusable components
│   └── styles/      # CSS files
├── .eslintrc.json
├── next.config.js   # Next.js configuration
├── package.json     # Project dependencies
└── README.md

3. React Fundamentals

Next.js is built on top of React, so understanding React fundamentals is essential.

Components

Components are the building blocks of React applications. They are reusable pieces of code that return HTML elements.

// Simple functional component
function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

// Using the component
<Greeting name="John" />

Props

Props (short for properties) are how you pass data to components.

function TodoItem({ task, completed, onToggle }) {
  return (
    <div className="todo-item">
      <input 
        type="checkbox" 
        checked={completed} 
        onChange={onToggle} 
      />
      <span style={{ textDecoration: completed ? 'line-through' : 'none' }}>
        {task}
      </span>
    </div>
  );
}

Hooks

Hooks are functions that let you "hook into" React state and lifecycle features.

import { useState, useEffect } from 'react';

function TodoList() {
  // State hook for managing tasks
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState('');

  // Effect hook for fetching tasks
  useEffect(() => {
    // Fetch tasks from API
    fetch('/api/tasks')
      .then(response => response.json())
      .then(data => setTasks(data));
  }, []); // Empty dependency array means this runs once when component mounts

  // Function to add a new task
  const addTask = () => {
    if (newTask.trim()) {
      setTasks([...tasks, { id: Date.now(), text: newTask, completed: false }]);
      setNewTask('');
    }
  };

  return (
    <div>
      <h1>Todo List</h1>
      <div>
        <input
          type="text"
          value={newTask}
          onChange={(e) => setNewTask(e.target.value)}
          placeholder="Add a new task"
        />
        <button onClick={addTask}>Add</button>
      </div>
      <ul>
        {tasks.map(task => (
          <li key={task.id}>
            <TodoItem 
              task={task.text} 
              completed={task.completed}
              onToggle={() => {
                setTasks(tasks.map(t =>
                  t.id === task.id ? { ...t, completed: !t.completed } : t
                ));
              }}
            />
          </li>
        ))}
      </ul>
    </div>
  );
}

4. Tailwind CSS Integration

Tailwind CSS is a utility-first CSS framework that allows you to build custom designs without leaving your HTML.

# Install Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Configure Tailwind in your tailwind.config.js:

module.exports = {
  content: [
    './src/app/**/*.{js,ts,jsx,tsx}',
    './src/components/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Add Tailwind directives to your CSS:

/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

Use Tailwind classes in your components:

function Button({ children, onClick }) {
  return (
    <button
      onClick={onClick}
      className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
    >
      {children}
    </button>
  );
}

5. Next.js Routing

Next.js provides a file-system based routing mechanism where the app directory defines routes:

src/app/
├── page.js            # Home route (/)
├── about/
│   └── page.js        # About route (/about)
├── tasks/
│   ├── page.js        # Tasks list route (/tasks)
│   └── [id]/
│       └── page.js    # Dynamic task route (/tasks/1, /tasks/2, etc.)
└── layout.js          # Root layout (applies to all routes)

Linking between pages:

import Link from 'next/link';

function Navigation() {
  return (
    <nav>
      <ul>
        <li>
          <Link href="/">Home</Link>
        </li>
        <li>
          <Link href="/tasks">Tasks</Link>
        </li>
      </ul>
    </nav>
  );
}

6. Data Fetching in Next.js

Next.js provides several methods for fetching data:

Server Components (Default in App Router)

// This component fetches data on the server
async function TaskList() {
  const res = await fetch('https://api.example.com/tasks', { cache: 'no-store' });
  const tasks = await res.json();
  
  return (
    <ul>
      {tasks.map((task) => (
        <li key={task.id}>{task.title}</li>
      ))}
    </ul>
  );
}

export default TaskList;

Client Components

'use client';

import { useState, useEffect } from 'react';

function TaskList() {
  const [tasks, setTasks] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchTasks() {
      try {
        const res = await fetch('/api/tasks');
        const data = await res.json();
        setTasks(data);
      } catch (error) {
        console.error('Error fetching tasks:', error);
      } finally {
        setLoading(false);
      }
    }

    fetchTasks();
  }, []);

  if (loading) return <p>Loading tasks...</p>;

  return (
    <ul>
      {tasks.map((task) => (
        <li key={task.id}>{task.title}</li>
      ))}
    </ul>
  );
}

export default TaskList;

7. Building Our Todo App Frontend

Let's structure our Todo app frontend:

  1. Components:

    • TaskList: Displays all tasks
    • TaskItem: Individual task with toggle and delete functionality
    • TaskForm: Form for adding new tasks
    • Layout: Common layout for all pages
  2. Pages:

    • Home: Landing page
    • Tasks: List of all tasks
    • Task Detail: View/edit a specific task

Here's a sample implementation of the TaskItem component using Tailwind CSS:

'use client';

function TaskItem({ task, onToggleComplete, onDelete }) {
  return (
    <div className="flex items-center justify-between p-4 mb-2 bg-white rounded shadow">
      <div className="flex items-center">
        <input
          type="checkbox"
          checked={task.completed}
          onChange={() => onToggleComplete(task.id)}
          className="w-5 h-5 mr-3 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
        />
        <span className={`text-lg ${task.completed ? 'line-through text-gray-500' : 'text-gray-800'}`}>
          {task.title}
        </span>
      </div>
      <div>
        <button
          onClick={() => onDelete(task.id)}
          className="px-3 py-1 text-sm text-white bg-red-500 rounded hover:bg-red-600"
        >
          Delete
        </button>
      </div>
    </div>
  );
}

export default TaskItem;

Practice Exercises

  1. Set up a new Next.js project with Tailwind CSS
  2. Create a simple landing page with a navigation menu
  3. Build a task list page that displays mock tasks
  4. Create a form to add new tasks to the list
  5. Implement client-side form validation
  6. Add the ability to mark tasks as complete and delete tasks

Additional Resources

Next Steps

Now that you've learned how to build the frontend of a web application with Next.js, you're ready to move on to Session 7: WebApp - Backend where you'll learn how to create a backend API using Django Ninja.

Clone this wiki locally