CategoryCode and architecture
Static, incremental or dynamic rendering in Next.js
In Next.js, the choice between static, incremental and dynamic rendering is a decision about how long the data may age. Static renders once at build. Incremental renders at build and rebuilds in the background after a window. Dynamic renders on every request. Most projects pick dynamic by default and pay for it.
What each mode does on the server
Rendering decides when the HTML is produced. Next.js makes prerendering the default for any component that doesn’t use a request-time API, according to the framework glossary.
- Static. The build produces HTML and the React Server Components payload, and the CDN serves that file. The server does no work when a visitor arrives.
- Incremental. The build prerenders the page, and the first request after the revalidation window still receives the stale copy while a new version generates in the background. The ISR guide documents that sequence, from the first stale response to the cached update.
- Dynamic. The server renders the page for the request. Components that read
cookies,headers,searchParamsor routeparamsopt into this mode, as the caching guide states.
What the visitor downloads
The visitor always downloads HTML. What changes is where the HTML comes from and how much JavaScript arrives with it.
A static page arrives from a machine near the reader, so the response doesn’t wait on a database. A dynamic page arrives after the server finished its work, so the response carries the latency of the slowest dependency on that page.
React states that Server Components render ahead of bundling, in an environment separate from the client app. In its own example, the libraries used to render markdown aren’t included in the bundle at all. The client receives the rendered output.
That doesn’t mean a static page ships no JavaScript. Client Components still ship a bundle and still hydrate in the browser, and hydration is main-thread work. React’s hydration reference describes the client taking over the server HTML and attaching event handlers to it.
Cache isn’t rendering
People tend to lump the two together, and that is where most of the confusion comes from.
Rendering answers when the HTML is produced. Caching answers whether a produced result is stored and reused. The Next.js caching guide defines caching as storing the result of data fetching and other computations so future requests are served faster, without doing the work again.
In other words, one doesn’t depend on the other.
- A dynamic page can read from a cached data source, so it renders per request without hitting the database per request.
- A static page can be re-rendered at build and still not be stored, if the delivery layer is told not to keep it.
- The client keeps its own cache for navigation, separate from both.
Since Next.js 16 the recommended model is Cache Components, and new apps from create-next-app have it on by default. The use cache directive caches either a data function or a whole component, each with its own lifetime. The rendering philosophy page draws the consequence: the line between static and dynamic now sits at the component, not the route. A page can ship a static shell and stream the request-time parts behind a Suspense boundary. Projects that stayed on the previous model still set caching per route segment, so check which model a codebase uses before reading its config.
Where the modes show up in Core Web Vitals
The yardstick is Core Web Vitals: main content loaded by 2.5 seconds, a response to input within 200 milliseconds and layout shift of 0.1 at most, read at the 75th percentile. The post on framework or static site explains each metric.
Static rendering moves the work to build time, so the response isn’t waiting on a query. That favors LCP, because the largest element can arrive early. The same page still runs hydration, and hydration competes for the main thread, which is where INP is decided.
Dynamic rendering moves the work to request time. The Google Web Vitals article names slow server response times as a diagnostic cause behind poor LCP. A page that queries three services before it renders inherits the slowest of the three on every visit.
Neither mode decides CLS. Layout stability depends on whether the shell reserves space for what arrives later, including content that streams in through a Suspense boundary.
What Nielsen Norman Group says about the wait
The framework decides when the HTML is ready. The reader only perceives the wait, and the Nielsen Norman Group has measured that side for decades.
The response-time limits are the oldest and most cited. Response Times: The 3 Important Limits sets 0.1 second as the point where a system feels instant and 1.0 second as the limit for an unbroken flow of thought. Ten seconds is the limit for holding attention.
Website Response Times updates that framing for the web. It states that users need a response in under 0.1 second for direct manipulation, and that a 10-second delay makes them switch tasks.
Progress Indicators Make a Slow System Less Insufferable adds the case where waiting is unavoidable. The article states that a result taking more than 10 seconds should come with an estimate, because users abandon the task instead of waiting.
Skeleton Screens 101 closes the gap with this post. It recommends the skeleton screen for a full page load of 2 to 10 seconds, which is exactly the window an uncached dynamic route falls into.
The four articles don’t tell one story about the same 10 seconds. The response-time article treats a long delay as a failure to avoid, and the progress-indicator article treats it as a case to design for with an estimate. The skeleton article sits between them, and it accepts the wait while insisting the layout reserve its space (NN/g, response times, NN/g, progress indicators, NN/g, skeleton screens).
When dynamic is the correct answer
When the data changes for each visitor, dynamic rendering isn’t a mistake. It is what the case calls for.
- Anything read from a session, a cookie or an authorization header. Next.js lists these as runtime APIs that need information available only at request time.
- Price, stock and availability that change within the same minute.
- Authenticated dashboards and any view built from the reader’s own records.
- Content where a stale answer is wrong rather than merely old, such as a payment status or a legal notice.
The Next.js documentation is explicit here. For components that need fresh data on every request, it says not to cache, and to stream them behind a Suspense boundary instead.
The cost of choosing dynamic by default
You pay the cost on every visit, and it rarely shows up as its own line on the bill.
- Every request runs the render again. The slowest dependency on the page sets the floor for the response.
- The edge network mostly idles. A static file can absorb a traffic spike, and a per-request render can’t.
- Compute is billed by request, so a page that changes once a day is paid for thousands of times.
- The team adds caching back later, one layer at a time, and rebuilds the behavior it had at build time with more parts.
None of this is waste when the data really is per user. The waste appears when nobody named the window in which the data may age, and dynamic was the path of least resistance.
How to make the call
Say, in seconds, how stale the data is allowed to get, and the rendering mode follows from that.
- Zero seconds, or a different answer per visitor. Dynamic.
- A window measured in hours or days. Prerender at build, then revalidate.
- Unknown. Start static and measure. Moving a route to dynamic later is a change in configuration, not a rewrite.
The framework default is a useful signal here. In Next.js, a page that never touches a request-time API is already prerendered, and that is the stated default in the documentation.
Rendering mode is a statement about the data, not about the framework. Pick how long the data can go without updating, and the mode stops being a matter of taste and becomes a consequence.
Sources
- Next.js, Glossary
- Next.js, Caching
- Next.js, Rendering Philosophy
- Next.js, Caching (Previous Model)
- Next.js, Incremental Static Regeneration
- React, Server Components
- React, hydrateRoot
- Google, Web Vitals
- Nielsen Norman Group, Response Times: The 3 Important Limits
- Nielsen Norman Group, Website Response Times
- Nielsen Norman Group, Progress Indicators Make a Slow System Less Insufferable
- Nielsen Norman Group, Skeleton Screens 101


