Frontend
Intermediate 18 min read Frontend

Next.js App Router

Next.js is the React framework for production: file-based routing, server components, static generation, ISR and edge-ready deployments. Learn the app/ directory conventions and data fetching model.

Next.jsApp Routerserver componentsRSCSSRISR
Prerequisites: React Fundamentals
Introduction

What is this?

Next.js is a full-stack React framework built on the App Router. A file in app/ becomes a route: page.tsx defines the UI, layout.tsx wraps shared chrome, and route.ts creates API endpoints. React Server Components render on the server by default, so pages stream HTML with less JavaScript shipped to the client.

Motivation

Why do you need it?

  • File-based routing removes routing boilerplate and makes every route colocated with its data.
  • Server Components render on the server, drastically cutting client JavaScript and time-to-interactive.
  • Static generation, SSR and ISR are chosen per route instead of per framework.
  • Automatic optimization — image resizing, font subsetting, code splitting — ships with the framework.
  • A single codebase serves HTML, API routes and middleware for edge behavior.
Think about it

Mental model

Imagine

A restaurant's prep kitchen and dining room

Think of

Server and client components

The kitchen (server) does the heavy prep and sends out finished dishes; the dining room (client) only handles the final touches like refills. Next.js decides what is prepped on the server and what needs live interactivity at the table.

Mechanics

How it works

  • A request matches a route by the file tree under app/; segments become URL paths.
  • layout.tsx renders once and wraps page.tsx, persisting navigation state across child routes.
  • Server Components run on the server and stream HTML; 'use client' marks components that render on the client with their JS bundle.
  • async page functions can await data directly — the server fetches, resolves and streams the result.
  • Next caches outputs per route and per data call; ISR revalidates static pages on a schedule or on demand.

Resolving a request through the App Router.

Browser requests /blog/post/1
app/blog/[slug]/page.tsx
Server Component awaits fetch
Stream HTML
Client hydration
Interactive page
Reference

Key commands

npx create-next-app@latest my-app

Scaffold a Next.js project with the App Router.

Success! Created my-app ...
npm run dev

Start the development server with fast refresh.

Ready in ... Local: http://localhost:3000
npm run build

Type-check, compile and produce static plus server output.

Route (app) /blog [ISR] ...
npm run start

Serve the production build.

npm run lint

Run ESLint with the next and react-hooks configs.

Hands-on

Examples

A blog page that fetches on the server

A blog page that fetches on the server
import Link from "next/link";
export default async function BlogIndex() {
const response = await fetch("https://api.example.com/posts", {
next: { revalidate: 3600 },
});
const posts = await response.json();
return (
<main>
<h1>Latest posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={"/blog/" + post.slug}>{post.title}</Link>
</li>
))}
</ul>
</main>
);
}

Because this is a Server Component (no 'use client'), the function is async and awaits fetch directly on the server. The revalidate option caches the response for an hour (ISR). Link performs client-side navigation, so the page loads only the data it needs.

Real world

In production

  • Marketing sites and documentation get static generation with ISR so pages are fast and search-friendly.
  • E-commerce and content products use dynamic rendering with streaming for personalized data.
  • Server Components fetch directly from databases and internal APIs, reducing client round-trips.
  • Vercel and self-hosted Node servers run the same app, with edge middleware for auth and geo logic.
Guidelines

Best practices

  • Default to Server Components and add 'use client' only where interactivity actually requires it.
  • Keep client components at the leaves so the interactive surface and JS bundle stay small.
  • Use next/image and next/font instead of raw img and font files for automatic optimization.
  • Model every data path with loading.tsx and error.tsx boundaries for streaming fallbacks.
  • Set explicit cache behavior with fetch options or the unstable_cache API instead of relying on defaults.
Watch out

Common mistakes

  • Adding 'use client' to the top of a page just to use a small interactive child.
  • Using window or document in a Server Component, which crashes server rendering.
  • Passing functions or Date objects from server to client components, which are not serializable.
  • Using the Pages Router conventions (getServerSideProps) inside the App Router, where they do nothing.
Root cause analysis

Common issues

Error
Error: Event handlers cannot be passed to Client Component props
Root cause

A function is passed from a server to a client component, which cannot serialize functions.

Fix

Define the handler inside the client component or pass plain data and wire the handler there.

Error
window is not defined
Root cause

A server component references the browser global.

Fix

Move the code into a client component, a useEffect, or guard with typeof window !== 'undefined'.

Error
Dynamic server usage ... this page could not be statically generated
Root cause

The route reads request-time data (headers, cookies) without opting in.

Fix

Mark the route dynamic with export const dynamic = 'force-dynamic' or move the request-time read into a server action.

When things break

Debugging tips

  • Read the dev overlay's error stack to find whether the error originated in a server or client render.
  • Run npm run build and read the printed route table to confirm which routes are static, dynamic or ISR.
  • Inspect the network panel to see whether the page streamed HTML before JavaScript arrived.
  • Use the React Server Components logo in DevTools to trace which components are client-rendered.
Interviews

Interview checkpoint

Practice

Hands-on lab

Build a Blog Index with the App Router
35 min · intermediate
In progress
Objectives
  • Scaffold a Next.js app
  • Create routes with the app/ directory
  • Fetch data in a Server Component
  • Add loading and error boundaries

Build a Blog Index with the App Router

35 min · intermediate

0/5 steps
0%
sandbox · Linux sandbox
$
Try these commands
Test yourself

Quick quiz

0/3
1

Which file defines the UI for a route in the App Router?

2

Server Components add their JavaScript to the client bundle.

3

What does next: { revalidate: 60 } do on a fetch?

Quick reference

Cheat sheet

File conventions
app/page.tsxHome route UI
app/layout.tsxShared layout wrapper
app/[slug]/page.tsxDynamic route segment
app/loading.tsxStreaming fallback UI
app/route.tsAPI endpoint handler
Data fetching
async function Page()Server component fetch
fetch(url, { next: { revalidate } })ISR caching
'use client'Opt into client render
cookies() / headers()Request-time server reads
Go deeper

Official documentation