Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Learn Final Project Build a Small Web App with Next.js | Section
Building Web Apps with Next.js

bookFinal Project Build a Small Web App with Next.js

Swipe to show menu

You have learned how to build pages, handle routing, work with server and client components, fetch data, and process user input.

Now it is time to combine everything into a real application.

In this final project, you will build a small multi-page web app using Next.js. The goal is to apply all the concepts from this block and create something that feels like a real product.

Project Overview

You will build a simple Products App with the following features:

  • A homepage with a list of products;
  • A dynamic product page;
  • Loading and error states;
  • A form to add a product;
  • A route handler to process form data.

This project covers the full flow of a modern Next.js application.

What You Will Build

Your app will include:

  • / → list of products;
  • /products/[id] → product details page;
  • /add-product → form to submit a new product.

Step 1: Display Products

Create a homepage that fetches and displays a list of products.

export default async function Page() {
  const res = await fetch("https://api.example.com/products");
  const products = await res.json();

  return (
    <ul>
      {products.map((product) => (
        <li key={product.id}>
          {product.name}
        </li>
      ))}
    </ul>
  );
}

Step 2: Create a Dynamic Product Page

Use a dynamic route to display product details.

export default async function ProductPage({ params }) {
  const res = await fetch(
    `https://api.example.com/products/${params.id}`
  );
  const product = await res.json();

  return <h1>{product.name}</h1>;
}

Step 3: Add Loading and Error States

Create:

loading.tsx
error.tsx

Use them to handle loading and error scenarios for your pages.

Step 4: Build a Form

Create a form to submit new products.

"use client";

import { useState } from "react";

export default function AddProductForm() {
  const [name, setName] = useState("");

  async function handleSubmit(e) {
    e.preventDefault();

    await fetch("/api/products", {
      method: "POST",
      body: JSON.stringify({ name }),
      headers: {
        "Content-Type": "application/json",
      },
    });
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <button type="submit">Add Product</button>
    </form>
  );
}

Step 5: Create a Route Handler

Handle the submitted data:

export async function POST(request: Request) {
  const data = await request.json();

  return Response.json({
    message: "Product added",
    data,
  });
}

What You Practiced

In this project, you applied:

  • File-based routing;
  • Dynamic routes;
  • Server components and data fetching;
  • Client components and forms;
  • Route handlers for backend logic;
  • Loading and error handling.
Everything was clear?

How can we improve it?

Thanks for your feedback!

Section 1. Chapter 34

Ask AI

expand

Ask AI

ChatGPT

Ask anything or try one of the suggested questions to begin our chat

Section 1. Chapter 34
some-alt