How to read Next.js you did not write
Every earlier module in this course taught you to write Next.js. This module teaches the other half: reading Next.js someone — or something — else wrote, and deciding whether it is correct. An AI assistant hands you code that compiles, that next build accepts, and that looks exactly like the App Router examples you have been studying. None of that proves it is correct. Next.js’s own failure modes are unusually quiet: a page that should be static silently becomes dynamic, a prop that should fail loudly serializes into undefined, an action with no auth check builds and deploys without one warning. The five steps below are the order that actually surfaces these failures, and every later lesson in this module drills one part of it.
flowchart TD A["1. Boundaries<br/>'use client' / 'use server' placement"] --> B["2. Caching model<br/>segment config vs cacheComponents"] B --> C["3. Auth<br/>inside every action and route handler"] C --> D["4. Error and redirect path<br/>try/catch around redirect()/notFound()"] D --> E["5. Config<br/>next.config.ts, env vars"]
1. Boundaries: where does 'use client' and 'use server' sit?
Section titled “1. Boundaries: where does 'use client' and 'use server' sit?”Before you read what a component does, read which side of the network it runs on. "use client" does not mark one component — it marks the entry point to a whole subtree of the module graph, and everything reachable by import from there ships to the browser. "use server" marks a function that compiles into a public HTTP endpoint, reachable by anyone who has the action ID, not only by your own form.
- What to look for: the file (or the nearest parent) that has
'use client'or'use server'as its first line; every prop that crosses from a Server Component into a Client Component; every hook (useState,useRouter,useFormStatus) and confirm the file that calls it is actually a Client Component. - The question to ask: does this directive sit as low in the tree as it needs to, or did it get put on a layout / page near the root because that was the easiest place to make the error go away?
- Which lesson explains it: Server and Client Components for the subtree rule and the serialization boundary; What Are Server Actions? for why every action is a public endpoint the moment it is exported.
2. The caching model this project actually runs
Section titled “2. The caching model this project actually runs”Next.js has shipped two different caching models across its life, and a single project only runs one of them at a time: Route Segment Config (dynamic, revalidate, fetchCache, dynamicParams, runtime — the model every pre-16 tutorial teaches) or Cache Components (cacheComponents: true in next.config.ts, 'use cache', cacheLife, cacheTag). They are mutually exclusive — every Route Segment Config export fails next build the moment cacheComponents is on. AI-generated code frequently mixes vocabulary from both models, because both appear in its training data.
- What to look for: open
next.config.tsfirst and check forcacheComponents: truebefore you read a single route. Then check whether the routes useexport const dynamic = .../export const revalidate = ...(old model) or'use cache'/cacheLife(...)/cacheTag(...)(new model). - The question to ask: does every file agree with what
next.config.tssays, or does this file assume the other model? - Which lesson explains it: Route Segment Config and Runtimes for the old model and the exact incompatibility error;
use cacheand Cache Components and Partial Prerendering for the new one.
3. Auth inside every action and every route handler
Section titled “3. Auth inside every action and every route handler”A Server Action is a POST endpoint; a Route Handler is whatever HTTP method you export. Neither one inherits protection from the page that renders a button pointing at it — render-time gating (only showing a delete button to an admin) controls the UI, not who can curl the endpoint directly.
- What to look for: the first lines of every function body inside
app/actions.ts, any'use server'file, and everyapp/**/route.ts. Look for a session check (this course’s own DAL pattern isverifySession()) before any read or write, and look for the mutation filtering by the current user’s ID, not only checking that someone is logged in. - The question to ask: if I sent this exact request with
curland someone else’s session cookie, whose data would move? - Which lesson explains it: What Are Server Actions? and Security and Patterns; Authentication Patterns for the DAL itself.
4. The error and redirect path
Section titled “4. The error and redirect path”redirect() and notFound() do not return — they throw a special Next.js control-flow error (NEXT_REDIRECT / NEXT_HTTP_ERROR_FALLBACK) that the framework catches higher up the tree. A try/catch wrapped around code that calls either of them will catch that internal throw too, unless it deliberately re-throws it — and the failure is invisible in development, because the page usually still renders something.
- What to look for: every
try { … } catch { … }block; does it contain a call toredirect()ornotFound(), directly or through a helper likeverifySession()? Does thecatchblock log-and-swallow, or does it callunstable_rethrow(err)/ re-throw? - The question to ask: if I hit this exact code path against a real running server, does it actually respond with a redirect / a 404 — or with a 200 and the wrong body?
- Which lesson explains it: Mutations and Revalidation, which proves this exact failure against a real server.
5. Config: next.config.ts and environment variables
Section titled “5. Config: next.config.ts and environment variables”The last thing to read, because it changes the meaning of everything above it. next.config.ts decides which caching model is active (step 2) and which experimental flags are on; .env* files decide which variables are available where, and only variables prefixed NEXT_PUBLIC_ — accessed as a static, literal process.env.NEXT_PUBLIC_X — are ever safe to reach from browser code.
- What to look for:
cacheComponents,reactCompiler,turbopack.*innext.config.ts; everyprocess.env.*read inside a file that has (or imports into)'use client'; everyNEXT_PUBLIC_-prefixed variable and what it actually holds. - The question to ask: does this variable need to reach the browser at all — and if it does, would I still be comfortable if it showed up, in plain text, inside a
.next/static/chunks/*.jsfile anyone can download? - Which lesson explains it: Environment and Configuration; Middleware and the Request Lifecycle for
proxy.tsand the request-time config that runs before any of the above.
Mental Model
Section titled “Mental Model”Reading order for a Next.js route you did not write:
1. Boundaries → where 'use client' / 'use server' actually sit, and what they pull in2. Caching model → segment config XOR cacheComponents — which one does this project run?3. Auth → does every action/route handler check the session AND the row's owner?4. Error/redirect→ does a try/catch anywhere swallow redirect()/notFound()?5. Config/env → next.config.ts flags, and which env vars reach the browser
Skipping straight to "does it build" answers none of these — all five compile clean.Common Misconceptions
Section titled “Common Misconceptions”- “If
next buildpasses, the code is correct.” Every failure mode in this module’s catalog builds clean.next buildproves the code is syntactically valid Next.js, not that it is safe or behaves as intended. - “Only the code inside the form matters for a Server Action.” The action is reachable by its ID from anywhere, independent of the form that happens to call it — see step 3.
- “A
try/catcharound risky code is always safer.” Around a call toredirect()ornotFound(), an unconditionalcatchis less safe: it turns a clean redirect/404 into a silently wrong 200 response. - “If a human wrote it, it needs less scrutiny than AI output.” Every pattern in this module’s catalog also appears in human-written Next.js code — AI just produces it with more confidence and at higher volume.