Next.js 16 brings a mature, production-ready App Router that changes how we think about React applications. In this guide, we'll build a complete project from scratch — no shortcuts.
Prerequisites
Before diving in, make sure you have:
- Node.js 20+ installed
- Basic familiarity with React and TypeScript
- A code editor (VS Code recommended)
Project Setup
Scaffold a new project with the official create-next-app CLI:
npx create-next-app@latest my-app --typescript --tailwind --app
cd my-app
npm run dev
Your project will be running at http://localhost:3000.
Understanding the App Router
The App Router is the heart of Next.js 16. Unlike the old pages/ directory, everything in app/ is a React Server Component by default.
File Conventions
src/app/
├── layout.tsx # Root layout (wraps every page)
├── page.tsx # Homepage → /
├── about/
│ └── page.tsx # About page → /about
└── blog/
├── page.tsx # Blog listing → /blog
└── [slug]/
└── page.tsx # Dynamic route → /blog/:slug
Server vs Client Components
This is the key mental model shift:
[!NOTE] Server Components run on the server at build time or request time. They can fetch data directly, read files, and access databases — but they cannot use
useState,useEffect, or event handlers.
[!TIP] Client Components are marked with
"use client"at the top of the file. They run in the browser and support interactivity. Keep them small and push them to the leaves of your component tree.
// Server Component (default) — no directive needed
export default async function BlogList() {
const posts = await db.posts.findMany(); // Direct DB access ✅
return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}
// Client Component — needs the directive
"use client";
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0); // useState ✅
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
Data Fetching
Server Components make data fetching dramatically simpler:
// No useEffect, no loading state, no API routes needed
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await fetch(`https://api.example.com/products/${params.id}`, {
next: { revalidate: 3600 }, // ISR: revalidate every hour
}).then(r => r.json());
return (
<div>
<h1>{product.name}</h1>
<p>${product.price}</p>
</div>
);
}
Layouts
Layouts persist between navigations — they don't unmount. This is perfect for headers, footers, and sidebars:
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Header />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
Static Generation with generateStaticParams
For dynamic routes, export generateStaticParams to pre-render pages at build time:
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getAllPosts();
return posts.map((post) => ({ slug: post.slug }));
}
Metadata API
Replace <Head> with the built-in Metadata API:
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "My Page",
description: "Page description for SEO",
openGraph: { title: "My Page", type: "article" },
};
Deployment
Push to GitHub and connect to Vercel for zero-config deployment:
git push origin main
# Vercel auto-detects Next.js and configures everything
Summary
Next.js 16 with the App Router gives you:
- Server Components for zero-bundle-size data fetching
- Nested layouts that persist between routes
- Static generation with
generateStaticParams - Built-in metadata for SEO
- TypeScript-first developer experience
Start with Server Components everywhere, add "use client" only when you need interactivity. That's the golden rule.
Tools in this post
Related Tool
JSON Parser & Formatter
Validate, format, and minify JSON data with error highlighting.
Try it freeRelated Tool
XML Formatter & Beautifier
Beautify and minify XML code with syntax highlighting.
Try it freeWritten by
DebuggerMe TeamThe DebuggerMe team builds developer tools, writes technical content, and helps teams ship better software.
Related Articles
All articles →React Server Components in Depth — What They Are and When to Use Them
React Server Components fundamentally change how we think about rendering. This guide breaks down how they work, how they differ from Client Components, and the patterns that will make your Next.js apps faster.
Why TypeScript Generics Are More Powerful Than You Think
A deep dive into TypeScript's generic type system — from basic usage to advanced patterns like conditional types, infer, and mapped types that will make your code safer and more expressive.
Stop Writing API Wrappers — Use TanStack Query Instead
Most frontend codebases have a homegrown API layer full of useEffect hacks, loading booleans, and stale data bugs. TanStack Query solves all of these in 20 lines. Here's how to migrate.