Rendering Strategies: SSR, SSG, ISR, Dynamic and Streaming
Learn how Next.js decides when and where pages are rendered, including static generation, dynamic rendering, revalidation, streaming, and partial user experiences.
Static Rendering
Static rendering means content can be generated ahead of the request and reused. This is ideal for pages whose output does not need to change for every visitor.
Examples include marketing pages, documentation, public blog articles, and product catalogs that do not require per-request personalization.
Static output can be delivered efficiently through a CDN because the server does not need to execute application logic for every request.
- Static rendering is excellent for stable public content.
- Static pages can be distributed through caching infrastructure.
- Static does not necessarily mean the source data can never change.
Dynamic Rendering
Dynamic rendering is appropriate when the response depends on request-specific information or data that must be resolved during the request.
Examples include personalized dashboards, authenticated pages, request-specific authorization, or content that must always be fresh.
Dynamic rendering should be used intentionally because it can increase server work and reduce caching opportunities.
- Use dynamic rendering when request-specific information is required.
- Personalization commonly requires dynamic execution.
- Do not make every page dynamic without a reason.
Incremental Revalidation
Revalidation allows previously generated content to be reused while still allowing it to become fresh periodically or when explicitly invalidated.
Imagine a news homepage that can tolerate content being several seconds or minutes old. Revalidation can provide a useful balance between freshness and performance.
The key architectural question is not simply 'SSR or SSG?' but 'how fresh does this data actually need to be?'
const response = await fetch('[https://example.com/api/products](https://example.com/api/products)', {
next: {
revalidate: 60
}
});
const products = await response.json();- Revalidation is useful when data changes but does not need request-level freshness.
- Choose freshness requirements based on business needs.
- Caching strategy should be part of application architecture.
Streaming and Suspense
Streaming allows a page to send useful UI to the browser before every piece of data is ready. React Suspense boundaries define areas that can show fallback content while slower server work completes.
A dashboard might render its navigation immediately while a slow analytics query continues. The user sees the shell first instead of waiting for the slowest operation.
Streaming is especially valuable when a page contains independent data sources with different response times.
import { Suspense } from 'react';
export default function Dashboard() {
return (
<main>
<h1>Dashboard</h1>
<Suspense fallback={<p>Loading analytics...</p>}>
<Analytics />
</Suspense>
</main>
);
}- Streaming improves perceived performance.
- Suspense lets slow portions of a page load independently.
- Do not create arbitrary Suspense boundaries; place them around meaningful slow or independent UI.
Route-Level Loading UI
A loading.tsx file provides a route-level loading experience. Next.js can use it while the corresponding route content is being prepared.
A good loading state should resemble the final page structure. Skeleton layouts are often better than generic spinners because they reduce layout shifts and communicate what is loading.
export default function Loading() {
return (
<div aria-busy='true'>
<div className='skeleton skeleton-title' />
<div className='skeleton skeleton-card' />
<div className='skeleton skeleton-card' />
</div>
);
}- Loading states are part of application UX.
- Skeletons can communicate structure better than generic spinners.
- Design loading states around realistic slow paths.