Mental Model First: Next.js is a React framework that moves work to the server — rendering, data fetching, caching, routing — so the client receives less JavaScript and more ready-to-use HTML. Every decision in Next.js is about where and when code runs: build time, server request time, or client.
These are two completely different routing paradigms. App Router (Next.js 13+) is the current standard. Pages Router still works and is in millions of production apps — you must understand both.
| Skill | Core Concepts | App Router (/app) |
Pages Router (/pages) |
Resources |
|---|---|---|---|---|
| File-based Routing | Folder/file structure defines URL structure — no router config needed | app/dashboard/page.tsx → /dashboard. Special files: page, layout, loading, error, not-found |
pages/dashboard.tsx → /dashboard. _app.tsx (global wrapper), _document.tsx (HTML shell) |
Next.js – App Router |
| Layouts | Persistent UI that wraps child routes. Layouts do not re-render on navigation — state is preserved | layout.tsx files nest automatically. Root layout required. Layouts can fetch their own data |
_app.tsx for global layout; custom layout pattern using per-page getLayout function |
Next.js – Layouts |
| Dynamic Routes | URL segments as parameters | app/blog/[slug]/page.tsx → params.slug; [...slug] for catch-all; [[...slug]] for optional |
pages/blog/[slug].tsx → useRouter().query.slug or getStaticProps({ params }) |
Next.js – Dynamic Routes |
| Nested Routes & Parallel Routes | Render multiple independent route segments in the same layout | Parallel Routes via @slot folders — render sibling pages (e.g., modal + page simultaneously). Intercepting Routes via (.) convention |
Not available | Next.js – Parallel Routes |
| Route Groups | Organize routes without affecting URL structure. Share layouts among a subset of routes | (marketing)/about/page.tsx → /about (parens excluded from URL) |
Not available | Next.js – Route Groups |
| Navigation | Client-side navigation without full page reload | <Link> component (prefetches on hover), useRouter() for programmatic nav, redirect() for server-side redirects |
<Link> component, useRouter().push(), router.replace() |
Next.js – Linking |
The core question: when is the HTML generated? Each strategy has different performance, freshness, and infrastructure tradeoffs.
| Strategy | When HTML is Generated | App Router Approach | Pages Router Approach | Best For |
|---|---|---|---|---|
| SSG – Static Site Generation | Once at build time | fetch(url, { cache: 'force-cache' }) inside async Server Component; generateStaticParams for dynamic paths |
getStaticProps + getStaticPaths |
Marketing pages, blogs, docs — content that rarely changes |
| SSR – Server-Side Rendering | On every request | fetch(url, { cache: 'no-store' }) or const data = await fetchData() with no caching in a Server Component |
getServerSideProps — runs on every request |
Dashboards with real-time data, personalized pages, auth-protected content |
| ISR – Incremental Static Regeneration | At build time, revalidated after N seconds or on-demand | fetch(url, { next: { revalidate: 60 } }) or export const revalidate = 60 at segment level |
getStaticProps with revalidate: 60 return value |
E-commerce product pages, news sites — mostly static but needs freshness |
| CSR – Client-Side Rendering | In the browser after JS loads | Add 'use client' + TanStack Query / SWR for data fetching — opt out of server rendering |
useEffect + fetch, or SWR/React Query on page component |
Highly interactive sections, real-time data (dashboards, charts, user feeds) |
| Streaming | Progressively from the server as components resolve | Wrap slow components in <Suspense fallback={...}>. loading.tsx auto-wraps the entire segment |
Not available natively | Pages with mixed fast/slow data sources — avoid blocking the whole page on one slow query |
The most important mental model in the App Router. All components are Server Components by default.
| Aspect | Server Components | Client Components |
|---|---|---|
| Where they run | Node.js server — never shipped to browser | Browser (+ server for initial HTML via SSR) |
| How to declare | Default — no directive needed | Add 'use client' at the top of the file |
| Can use | async/await, direct DB access, secrets/env vars, server-only APIs |
Hooks (useState, useEffect), browser APIs, event listeners |
| Cannot use | Hooks, browser APIs, window, event handlers |
Direct DB access, server-only modules |
| Bundle impact | Zero JS added to client bundle | JavaScript is shipped to and executed by the browser |
| When to use | Layout, data fetching, non-interactive UI | Interactive UI, forms, animations, anything that responds to user input |
| Composition rule | Server Components can import Client Components | Client Components cannot import Server Components (but can receive them as children props) |
App Router fundamentally changed data fetching. The extended
fetchAPI and React's cache system replacegetStaticProps/getServerSidePropsentirely.
| Skill | Core Concepts | Tools & Techniques | Key Details | Resources |
|---|---|---|---|---|
| Server Component Fetching | async/await directly in the component body — no useEffect, no loading state needed. React deduplicates identical fetch calls automatically |
Extended fetch with cache options |
cache: 'force-cache' (SSG), cache: 'no-store' (SSR), next: { revalidate: N } (ISR). Fetch at the component level, not the page level — only fetch what each component needs |
Next.js – Data Fetching |
| Server Actions | Next.js 14+ feature. Async server functions called directly from forms or client components — no API route needed for mutations. Replace most POST endpoints | 'use server' directive (inline or in a separate file) |
Form action={serverAction}, useFormState / useActionState for progressive enhancement, automatic revalidation with revalidatePath / revalidateTag |
Next.js – Server Actions |
| Route Handlers | API endpoints inside the App Router. Replace pages/api/ routes. Used for webhooks, OAuth callbacks, or when you need a real HTTP endpoint |
app/api/route/route.ts with exported GET, POST, PATCH, DELETE functions |
Request/Response Web API, no Express needed, streaming responses supported, NextRequest / NextResponse helpers |
Next.js – Route Handlers |
| ORM / Database Access | Query the database directly from Server Components or Server Actions — no intermediary API layer needed | Prisma (type-safe, popular), Drizzle (lightweight, SQL-first), Mongoose (MongoDB) | Keep DB logic in a lib/db.ts or server/ folder; never import server-only modules into Client Components; use server-only package to guard |
Prisma + Next.js |
| Caching & Revalidation | Next.js has a multi-layer cache. Understanding it prevents stale data bugs | revalidatePath, revalidateTag, unstable_cache |
Tag fetches with next: { tags: ['products'] } → call revalidateTag('products') on mutation for surgical cache invalidation |
Next.js – Caching |