Skip to content

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"]
Reading order for a route you did not write

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.ts first and check for cacheComponents: true before you read a single route. Then check whether the routes use export 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.ts says, 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 cache and 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 every app/**/route.ts. Look for a session check (this course’s own DAL pattern is verifySession()) 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 curl and 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.

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 to redirect() or notFound(), directly or through a helper like verifySession()? Does the catch block log-and-swallow, or does it call unstable_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.* in next.config.ts; every process.env.* read inside a file that has (or imports into) 'use client'; every NEXT_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/*.js file anyone can download?
  • Which lesson explains it: Environment and Configuration; Middleware and the Request Lifecycle for proxy.ts and the request-time config that runs before any of the above.
Reading order for a Next.js route you did not write:
1. Boundaries → where 'use client' / 'use server' actually sit, and what they pull in
2. 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.
  • “If next build passes, the code is correct.” Every failure mode in this module’s catalog builds clean. next build proves 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/catch around risky code is always safer.” Around a call to redirect() or notFound(), an unconditional catch is 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.
Why does this module tell you to check next.config.ts for cacheComponents *before* reading any route file?
What does a real running server return when post is missing?
try {
  const post = await getPost(id)
  if (!post) notFound()
  return post
} catch (err) {
  console.error(err)
  return null
}
A Server Action checks nothing before writing to the database, but the button that calls it only renders for logged-in admins in the UI. Is the action safe?
Where does NEXT_PUBLIC_-prefixed data end up if it is read inside a Client Component?
What happens when this project runs next build?
// next.config.ts
const nextConfig = { cacheComponents: true }

// app/page.tsx
export const dynamic = 'force-dynamic'
Which order does this lesson recommend for reading a Next.js route you did not write?