skip to content

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