Technical
Next.js Caching Strategies I Trust in Production
Next.js caching changed shape in every recent version. The App Router's fetch cache, Data Cache, Route Cache, Router Cache; each does something different, and developers conflate them weekly. After shipping a production blog with real cache invalidation needs, here is the mental model that survived contact with reality.
The Four Caches in One Sentence
Data Cache stores fetch results on the server. Full Route Cache stores rendered HTML on the server. Router Cache stores navigation state in the browser. Request Memoization dedupes within a single render.
Conflating any two is where most production bugs start. Each has its own invalidation rules.
The Strategy I Use
Static content with long TTL: static generation. Blog posts, landing pages, author bios. Rebuild nightly with revalidate: 86400, or on publish via revalidatePath. The Full Route Cache does the work. Pages serve from the edge and cost nothing.
Dynamic content with moderate staleness tolerance: ISR. Lists, tag pages, archives. revalidate: 60 gives fresh data within a minute without server-side overhead per request. This is the sweet spot for most content sites.
Per-request dynamic: no cache. Search, authenticated dashboards, forms. Mark the route dynamic and let Next.js render on demand.
// static blog post
export const revalidate = 86400;
// live archive, stale-while-revalidate-ish
export const revalidate = 60;
// truly dynamic
export const dynamic = 'force-dynamic';Explicit Invalidation
revalidatePath and revalidateTag in route handlers let me invalidate on publish without waiting for the next revalidate window. This is what turns ISR from 'eventually consistent' to 'fast and fresh on real actions'. See the Next.js caching docs for the canonical reference.
What Broke in Production
I used fetch without a next option in a server component that was rendered inside a client component. The Data Cache and the Router Cache disagreed. Users saw stale data after actions. Fix: be explicit about every fetch's caching behavior. Defaults drift between versions; explicitness is durable.
The Rule of Thumb
Pick one caching tier per route and make it explicit. Mixed strategies on the same route are where the bugs live. Simple, explicit, and re-readable a year from now beats clever.
The Publishing Webhook in Practice
My publish flow ends with a call to a protected /api/revalidate route that runs revalidatePath('/blog/[slug]') and revalidatePath('/blog'). The call is idempotent and fast. Authors see their changes live within seconds of hitting publish in the CMS. Without the webhook, they would wait for the ISR window and lose trust in the system. Webhook-driven invalidation is what makes ISR feel like a dynamic site at a static site's cost.
RELATED READING
The Consulting Shift I Am Making In Year Two
After a year of writing and building, my consulting practice is changing shape. Shorter engagements. Sharper outcomes.
ReadThe Frontend Shift: Shipping Less JavaScript In Year Two
A year ago I reached for Next.js for everything. This year I often reach for nothing.
ReadThe Serverless Lesson I Would Write On A Sticky Note
After a year of shipping serverless projects, one rule explains most of the wins and all of the losses.
Read