The App Router rewrote the rules for caching, state, and rendering. Teams ship stale-data bugs before they understand the new model. The errors below passed code review and cost days of debugging in real projects.
Aggressive caching by default
fetch requests enter the Data Cache and route segments stay cached until someone says otherwise. Stale data bugs trace back to defaults nobody chose. force-dynamic, revalidatePath, and revalidateTag become daily vocabulary; learn each one before your first deploy. The Next.js documentation describes Data Cache, Full Cache, and Router Cache.
The boundary serializes props
Functions, class instances, and complex objects throw serialization errors when crossing the Server-to-Client boundary. Composition with children solves most cases: the Server Component builds the tree and passes finished JSX down. For executable logic on the client, inject custom hooks instead of pushing functions across the line.
Serialized request waterfalls
Nested await calls inside Server Components turn parallel work into a waterfall: the product fetch waits on the user fetch, which waits on the session. Promise.all parallelizes what has no dependency between calls, and Suspense boundaries restore streaming for whatever can arrive later. A three-level waterfall multiplies network latency by three.
Deterministic hydration
Date objects and random values diverge between the server render and the client render, producing mismatch warnings. Serialize deterministically: ISO string timestamps, IDs generated outside render, values formatted with a fixed locale. The mismatch warnings stop.
Route Handlers: streaming without API Routes
App Router handlers stream out of the box. Cookies and headers change shape compared with the Pages Router: the Next.js documentation details the new request and response APIs. Middleware runs before everything, static assets included, unless the matcher config excludes those paths. Without exclusion, every image request walks through your auth middleware.
Server Actions expose POST endpoints
Server Actions simplify forms yet still expose POST endpoints. Check authentication and authorization inside every action. Redirect after the mutation to prevent double submits on refresh. Validate input with zod the way you would on a public API.
Bloated bundle from a wrong import
Importing a server-side library into a Client Component bloats bundles until builds fail or pages crawl. The server-only package enforces the boundary at import time and turns forgetfulness into a build error. Run the webpack analyzer once in a while; bundle drift arrives without notice.
What changes at deployment time
- Self-hosting needs the Node runtime for ISR and image optimization
- The edge runtime restricts libraries: no fs, no native database drivers
- Platform-specific features require adapters outside Vercel
Enjoyed this content?
I build web products and AI solutions the right way — solid architecture, maintainable code, and real delivery.
Let's talk