Next js

Module 12: Real-World Project in Next.js 14 with TypeScript

Build a Complete E-Commerce App with Auth, Cart, CRUD, Zustand, Server Actions & Deployment

To solidify your skills, youโ€™ll now build a fully working E-Commerce Web App. This project will include:

  • Product listing and detail pages
  • Add to Cart (Zustand)
  • Auth with next-auth
  • Admin-only product management (CRUD)
  • Form handling via Server Actions
  • Responsive layout + Tailwind styling
  • Deployment to Vercel

๐Ÿงฑ Folder Structure Overview

src/
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ page.tsx                 โ†’ Home (Product Grid)
โ”‚   โ”œโ”€โ”€ product/[id]/page.tsx    โ†’ Product Detail Page
โ”‚   โ”œโ”€โ”€ admin/                   โ†’ Admin Dashboard (Protected)
โ”‚   โ”œโ”€โ”€ cart/page.tsx            โ†’ Cart Page
โ”‚   โ”œโ”€โ”€ api/products/route.ts    โ†’ CRUD API (GET, POST, etc.)
โ”œโ”€โ”€ components/
โ”œโ”€โ”€ lib/                         โ†’ DB (Prisma) and utils
โ”œโ”€โ”€ store/                       โ†’ Zustand cart store
โ”œโ”€โ”€ server/                      โ†’ Server Actions

๐Ÿ›’ 1. Product Listing Page (Home)

๐Ÿ”น src/app/page.tsx

import { getAllProducts } from "@/server/actions/product";
import ProductCard from "@/components/ProductCard";

export default async function HomePage() {
  const products = await getAllProducts();

  return (
    <main className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 p-6">
      {products.map((product) => (
        <ProductCard key={product.id} product={product} />
      ))}
    </main>
  );
}

๐Ÿ“ฆ 2. Cart Functionality with Zustand

๐Ÿ”น src/store/cart.ts

import { create } from "zustand";

type CartItem = { id: number; name: string; price: number; qty: number };

type Store = {
  items: CartItem[];
  add: (item: CartItem) => void;
  remove: (id: number) => void;
};

export const useCart = create<Store>((set) => ({
  items: [],
  add: (item) =>
    set((state) => {
      const exists = state.items.find((i) => i.id === item.id);
      if (exists) {
        return {
          items: state.items.map((i) =>
            i.id === item.id ? { ...i, qty: i.qty + item.qty } : i
          ),
        };
      }
      return { items: [...state.items, item] };
    }),
  remove: (id) => set((state) => ({ items: state.items.filter((i) => i.id !== id) })),
}));

๐Ÿ”น Add to Cart in Product Page

"use client";
import { useCart } from "@/store/cart";

export default function AddToCart({ product }) {
  const { add } = useCart();
  return (
    <button
      onClick={() => add({ ...product, qty: 1 })}
      className="bg-green-600 text-white px-4 py-2 rounded"
    >
      Add to Cart
    </button>
  );
}

๐Ÿ” 3. Authentication with next-auth

  • Already covered in Module 9
  • Used to restrict access to /admin routes
  • Only allow admins to add/edit/delete products

๐Ÿงฐ 4. Admin Panel (CRUD with Server Actions)

๐Ÿ”น Add Product Form

'use client';
import { addProduct } from '@/server/actions/product';

export default function AddProductForm() {
  return (
    <form action={addProduct} className="space-y-4">
      <input name="name" required placeholder="Name" className="border p-2" />
      <input name="price" type="number" required placeholder="Price" className="border p-2" />
      <button className="bg-blue-600 text-white px-4 py-2">Add Product</button>
    </form>
  );
}

๐Ÿ”น Server Action (DB Insert)

// src/server/actions/product.ts
'use server';

import { prisma } from '@/lib/prisma';

export async function addProduct(formData: FormData) {
  const name = formData.get('name') as string;
  const price = Number(formData.get('price'));
  await prisma.product.create({ data: { name, price } });
}

export async function getAllProducts() {
  return prisma.product.findMany();
}

๐Ÿ’ฐ 5. Checkout Summary Page

๐Ÿ”น src/app/cart/page.tsx

'use client';
import { useCart } from '@/store/cart';

export default function CartPage() {
  const { items, remove } = useCart();

  const total = items.reduce((sum, item) => sum + item.price * item.qty, 0);

  return (
    <div className="p-6 space-y-4">
      <h2 className="text-xl font-bold">Cart</h2>
      {items.map((item) => (
        <div key={item.id} className="flex justify-between">
          <p>{item.name} x {item.qty}</p>
          <button onClick={() => remove(item.id)} className="text-red-600">Remove</button>
        </div>
      ))}
      <hr />
      <p className="text-lg">Total: โ‚น{total.toFixed(2)}</p>
    </div>
  );
}

๐ŸŒ 6. Deploy the Project on Vercel

  • Connect GitHub repo โ†’ Deploy on Vercel
  • Set .env.local for database and NEXTAUTH_SECRET
  • Set up a domain (optional)
  • Enjoy your live full-stack app!

โœ… Summary

Feature Implemented With
Routing & Layout App Router, layout.tsx
Reusable UI Tailwind + Components
Auth next-auth
Cart State Zustand
Server CRUD Server Actions + Prisma + PostgreSQL
Admin Panel Protected route + role-based access
Deployment Vercel + GitHub + Env Vars

๐Ÿ Congratulations! Youโ€™ve Built a Full-Stack App in Next.js 14 ๐ŸŽ‰

You now have the skills to build and deploy real-world projects using:

  • App Router
  • Server Components
  • Server Actions
  • State Management (Zustand + Redux)
  • Auth, APIs, CMS, SEO, and more

๐Ÿ“ฆ Bonus Modules (Optional)

  • ๐Ÿงช Testing with Playwright/Vitest
  • ๐Ÿ’ฌ Live Chat with WebSockets (e.g., Pusher)
  • ๐ŸŒ i18n (Multi-language Setup)
  • ๐Ÿ“ฑ Build a PWA (Progressive Web App)

Leave a Reply

Your email address will not be published. Required fields are marked *