skip to content

Opting Out of Caching

Sometimes the fix is to stop caching, and there are several levers at different scopes — one fetch, one segment, or the whole route. Interviewers follow up on what you gave up: every request now pays full render cost with no CDN hit.

part ofNext.jsoverview, primer and where to startread it →
on this pageshow

explore

questions

5

In a Next.js App Router page.tsx, what does `export const dynamic = 'force-dynamic'` do, and when would you add it?

level: juniorimportance: must knowfreq 62%

answer

  1. a Route Segment Config export
  2. chooses a rendering mode
  3. prerendering turned off for that segment
  4. server render on every request
  5. no stored HTML to replay

basics

~20 s

It is a Route Segment Config export that forces the route to be rendered on the server for every request instead of being prerendered, so its HTML is never served out of Next's Full Route Cache.

solid answer

~50 s

`export const dynamic = 'force-dynamic'` is one of the Route Segment Config exports you can put in a `page.tsx`, `layout.tsx`, or `route.ts`. It tells Next not to prerender that segment: instead of producing HTML at build time (or reusing a stored render), Next runs the server render again on every incoming request. You reach for it when the route genuinely has to reflect something that only exists per request and the page would otherwise be frozen at build time. The important nuance is that it is a *rendering* decision, not a per-fetch cache setting — so still say explicitly how each `fetch` in the page should be cached rather than assuming this export decides that for you. And it is not free: every request now pays a full server render plus whatever data that render waits on.

go deeper

for a junior

Be ready to state in one sentence that this export stops the route being prerendered and makes it render on the server per request, and to name the files it can go in: page.tsx, layout.tsx, route.ts.

for a middle

Explain what it does not do — it picks a rendering mode, it is not a per-fetch cache option and it does not clear stored data — and describe how placement in a layout widens the blast radius to every nested route.

for a senior

Treat it as a cost decision you can defend: say what you measured before and after, why a narrower per-fetch or per-component opt-out was not sufficient, and how the route's origin load now scales with traffic.

for a principal

Own the policy: which routes are allowed to be dynamic by default, how that is reviewed, and what capacity and cost assumptions break when high-traffic pages stop being served from prerendered output.

## What the export actually is Next's App Router lets a route segment configure itself by exporting specific named constants from `page.tsx`, `layout.tsx`, or `route.ts`. These are called Route Segment Config options, and `dynamic` is one of them: ```tsx // app/orders/page.tsx export const dynamic = 'force-dynamic' export default async function Page() { return <main>rendered on every request</main> } ``` The accepted values are `'auto'` (the default), `'force-dynamic'`, `'error'`, and `'force-static'`. `'force-dynamic'` is the deliberate opt-out: it says "do not prerender this segment; render it at request time." ## Static vs dynamic rendering, in one paragraph A route that is *statically rendered* is executed ahead of the user — at build time, or once and then reused — and the resulting HTML/RSC payload is stored and replayed. That stored render is what Next calls the Full Route Cache, and it is the reason a Next site can serve pages without touching your server or your database at all. A route that is *dynamically rendered* runs the component tree fresh on each request, so nothing about it can come out of that stored render. `force-dynamic` moves a route from the first category to the second. That is its entire job. ## When you actually want it - The page must show something that only exists at request time and there is no prerenderable version of it worth shipping. - The page is per-user and there is no meaningful shared HTML to store. - You are debugging a staleness bug and want to eliminate rendering-level caching as a variable before narrowing the fix down again. Notice how narrow that list is. `force-dynamic` is a blunt instrument at *segment* scope — every data read in that segment re-runs, not just the one that was stale. ## What it does not do Three misreadings show up constantly in interviews: 1. **It is not related to dynamic route segments.** A folder named `app/orders/[id]` is a *dynamic segment* — a URL parameter. The `dynamic` config export is about *rendering mode*. The word collision is unfortunate and interviewers enjoy it. 2. **It is not a global cache purge.** It does not empty Next's Data Cache, it does not invalidate anything you previously stored, and it does not reach into other routes. It changes how *this* segment renders. 3. **It is not a per-request cache setting for your `fetch` calls.** Caching of individual data reads is decided by the options you pass to those reads. Treat rendering mode and data caching as two separate decisions you make explicitly; that habit survives version changes in Next's defaults, whereas "force-dynamic sorts out my fetches too" does not. ## Where you put it matters Placement decides blast radius. In a leaf `page.tsx` it governs that one route. In a `layout.tsx` it governs the routes rendered inside that layout — and the root layout is rendered inside *every* route, so a `force-dynamic` there means nothing in the app can be served as a prerendered document any more. That is the single most common way a Next app quietly loses all of its static output. It also has no effect in a file marked `'use client'`. Route Segment Config is read from server-rendered route files; exporting it from a client component is silently useless, which makes for a frustrating afternoon. ## The cost A prerendered route can be served from a stored artifact — often from a CDN edge, with no server work at all. A dynamic route cannot. Every request now: - runs your server components again, - waits on whatever those components await, - and produces bytes that a shared cache in front of you cannot reuse unless you separately tell it to. So TTFB becomes "however long your data layer takes", and your origin capacity now scales with traffic instead of with how often content changes. That is a real trade, and it is exactly the follow-up an interviewer asks after you explain the mechanism: *what did you give up?* The good answer is that you reach for the narrowest opt-out that fixes the actual bug, and you only go segment-wide when the whole segment truly is per-request.

  • Does exporting it from a file that starts with 'use client' do anything?
    No. Route Segment Config is read from the server-rendered route files — `page.tsx`, `layout.tsx`, `route.ts`. Exporting `dynamic` from a client component is silently ignored, so the route keeps whatever rendering mode it had. This is a common source of "I set force-dynamic and nothing changed" reports; check which file the export actually lives in.
  • What changes if you put it in the root layout instead of one page?
    The root layout is rendered inside every route, so making it dynamic means no route in the app can be served as a prerendered document. You lose static output everywhere, and origin load jumps for pages that never needed freshness. Keep the opt-out at the lowest segment that actually needs it.
  • How would you confirm the route really is rendering per request?
    Check the `next build` output, which labels each route as prerendered or rendered on demand — that is the authoritative answer for the deployed build. In development, logging a timestamp or a request-scoped value in the server component and watching it change on every reload is a quick sanity check, but the build output is what you cite.

saying these in an interview costs you the question

  • Confuses it with dynamic route segments like [id]
  • Claims it purges Next's Data Cache app-wide
  • Thinks it also decides how each fetch is cached
  • Exports it from a 'use client' file and expects an effect
  • Puts it in the root layout to fix a single page

context

open as a page

A Next.js App Router dashboard showed one stale number, and a teammate fixed it by adding `export const dynamic = 'force-dynamic'` to the page — but only one of that page's six data reads has to be fresh. What narrower opt-outs were available, and what did the broad fix cost?

level: middleimportance: should knowfreq 52%

basics

~20 s

Opt out at the narrowest scope that fixes the bug: no-store on the one fetch, or connection() in the one component that must run per request. Forcing the whole segment dynamic re-runs all six reads on every request and ends prerendering for the route.

open as a page

After a stale-data fix, a Next.js App Router app that used to prerender most of its routes now builds with almost every route rendered on demand, and TTFB and origin CPU are up. How do you find what turned prerendering off, and how do you get it back?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Read the build output to see exactly which routes went dynamic, then look for an opt-out placed too high: force-dynamic or revalidate = 0 in a shared or root layout, an awaited connection() in a shared component, or a no-store buried in a helper every page imports. Push it down to the one place that needs it.

open as a page

What does `connection()` from `next/server` do when awaited in a Next.js App Router server component, and what does it solve that a fetch cache option cannot?

level: middleimportance: nice to knowfreq 32%

basics

~20 s

Awaiting connection() tells Next that rendering must wait for a real user request, so everything after it is excluded from prerendering. It is the opt-out for code with no data fetch at all, such as a component reading the clock or a random value.

open as a page

After a stale-pricing incident, a team adopts a rule that every Next.js App Router route carries `export const dynamic = 'force-dynamic'` so nothing can ever be stale. How would you evaluate that policy?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

The rule buys less correctness than it looks like and costs more than the team expects: rendering per request does not guarantee a user sees fresh data, while every route now pays a server render and origin capacity scales with traffic instead of with content change.

open as a page