Static vs Dynamic Rendering: Next.js Caching Strategies
Static rendering and dynamic rendering are the two fundamental rendering modes in Next.js, determining whether pages are pre-built at deploy time or computed on each request. Static rendering pre-builds pages once, serving them instantly from a global CDN to every user—best for content that changes rarely. Dynamic rendering computes pages per request, allowing personalization and real-time data—necessary for user-specific or frequently-changing content. Most production apps combine both, choosing per-page based on freshness requirements.
Static Rendering: Build-Time Pre-Rendering
Static rendering means Next.js builds your pages into HTML files during the build step (npm run build). These files are then deployed to a CDN and served to every user identically. The page is not recomputed; it's served as-is until the next deployment.
Static rendering is the default in Next.js. If your Server Component doesn't access request-specific data (cookies(), headers(), searchParams, or URL parameters), Next.js automatically renders it statically:
// app/about/page.js (automatically static)
export default async function AboutPage() {
const company = await fetch('https://api.example.com/company', {
next: { revalidate: false }
}).then(r => r.json());
return (
<div>
<h1>About {company.name}</h1>
<p>{company.description}</p>
</div>
);
}
At build time, this fetch executes once, the result is embedded in the HTML, and the page is cached. Every user sees the same page, served in under 50 ms from the CDN. No server work, no network request to fetch data—just a static file.
Benefits of static rendering:
- Instant delivery: Cached globally on CDNs, <50 ms response time.
- Reduced infrastructure: No server-side computation; costs less.
- Better SEO: Google crawls complete HTML with data included.
- Offline-friendly: Pages can be served even if your origin is down.
Drawbacks:
- Stale data: Content doesn't update until redeployment.
- Not user-specific: All users see the same page.
- Slow build times: Building thousands of pages takes time.
Use static rendering for:
- Marketing sites (about, pricing, contact)
- Blogs and documentation (changes infrequently)
- Product catalogs (same for all users)
- Public landing pages
Dynamic Rendering: On-Request Computation
Dynamic rendering means Next.js computes the page for each user request. If your Server Component accesses request-specific data, the page automatically becomes dynamic:
// app/dashboard/page.js (automatically dynamic due to cookies())
import { cookies } from 'next/headers';
export default async function DashboardPage() {
const sessionCookie = cookies().get('session')?.value;
const user = await fetch('https://api.example.com/user', {
headers: { Cookie: `session=${sessionCookie}` },
next: { revalidate: 300 }
}).then(r => r.json());
return (
<div>
<h1>Hello, {user.name}</h1>
<p>Your score: {user.score}</p>
</div>
);
}
This page is dynamic because it reads cookies(). For each request, Next.js renders a new page with that user's session. The page is not pre-built; it's computed per request, so each user sees their own data. The fetch is still cached (for 300 seconds), so repeated requests from the same user reuse the cache.
Benefits of dynamic rendering:
- Always fresh: Data is current as of the request time.
- User-specific: Each user sees their own data.
- Real-time capable: Can show live data (prices, weather, user activity).
Drawbacks:
- Slower TTFB: Server must compute page per request, adding 50–200 ms latency.
- Higher infrastructure costs: Server runs for every request.
- Not CDN-cacheable: Each unique user gets a unique response.
Use dynamic rendering for:
- User dashboards and accounts
- Personalized recommendations
- Real-time data (stock prices, live chat)
- Search results (filtered by query)
- Shopping carts
How Next.js Decides: Static vs Dynamic
Next.js automatically chooses based on your component code:
| Data Access | Rendering Mode | Why |
|---|---|---|
| None (or static imports only) | Static | Data doesn't vary per request |
fetch(url, { next: { revalidate: false } }) | Static | Explicitly static |
cookies(), headers(), searchParams | Dynamic | Request-specific data |
Database query with user ID from searchParams | Dynamic | Result varies per request |
revalidate: 0 (no caching) | Dynamic | No cache means compute per request |
Opt-out of automatic static rendering with dynamic = 'force-dynamic':
// app/realtime/page.js
export const dynamic = 'force-dynamic';
export default async function RealtimePage() {
const prices = await fetch('https://api.example.com/prices', {
next: { revalidate: 0 } // Always fresh
}).then(r => r.json());
return <div>Stock prices: {JSON.stringify(prices)}</div>;
}
Using dynamic = 'force-dynamic' and revalidate: 0 together ensures no caching whatsoever—every request fetches fresh data.
Incremental Static Regeneration (ISR): The Hybrid Approach
ISR bridges static and dynamic rendering. You pre-build pages at deploy time (fast CDN delivery), but automatically regenerate them on a schedule (fresh data):
// app/blog/[slug]/page.js
export const revalidate = 3600; // Regenerate every hour
export default async function BlogPostPage({ params }) {
const post = await fetch(`https://api.example.com/posts/${params.slug}`, {
next: { revalidate: 3600 }
}).then(r => r.json());
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
With revalidate: 3600, Next.js builds this page once. For the first hour, it serves the cached page from the CDN (static). After one hour, the next request triggers a background regeneration. During regeneration, users still see the old cached page (no delay). Once regeneration completes, new requests see the fresh page. This combines the speed of static with the freshness of dynamic.
ISR is ideal for:
- Blog posts (update hourly, not per-request)
- Product pages (data changes daily, not minute-by-minute)
- News articles (fresh within hours)
| Mode | Build Time | Per-Request Time | Data Freshness | Best For |
|---|---|---|---|---|
| Static | 1–5 min (build all pages) | <50 ms (CDN) | Stale until redeployment | Marketing, docs, catalogs |
| Dynamic | Fast (nothing built) | 100–300 ms (compute) | Always current | Dashboards, real-time data |
| ISR | 1–5 min (build all) | <50 ms (CDN, after revalidate) | Fresh on schedule | Blogs, product pages |
Mixed Strategies: Hybrid Applications
Real applications combine all three. A typical e-commerce site:
- Static: Homepage, category pages, product descriptions (same for all users, change rarely)
- ISR: Product listing pages (revalidate hourly to reflect new products)
- Dynamic: Shopping cart, checkout, user account (per-user, real-time inventory)
// Homepage: Static (no revalidate, never fetches request data)
// app/page.js
const products = await fetch('https://api.example.com/featured', {
next: { revalidate: false }
});
// Product listing: ISR (regenerate every 30 minutes)
// app/products/page.js
export const revalidate = 1800;
const products = await fetch('https://api.example.com/products');
// User account: Dynamic (per-user)
// app/account/page.js
import { cookies } from 'next/headers';
const user = await fetch('https://api.example.com/user', {
headers: { Cookie: cookies().toString() }
});
This mix optimizes for both performance (static pages are fast) and freshness (ISR and dynamic pages update on schedule or per-request).
Build Time Implications
Static pages are pre-built at deploy time. If you have 10,000 product pages, the build must generate all 10,000 HTMLs—this takes 5–20 minutes (depends on fetch times). For very large sites, this can become a problem. Solutions:
- Use On-Demand ISR: Build pages on-demand the first time they're requested (Vercel feature, 2026)
- Limit static pages to top-performing products (most-visited)
- Use dynamic rendering for tail pages
Vercel documentation (2025) recommends keeping static builds under 5,000 pages for fast deployments. Beyond that, consider hybrid approaches.
Key Takeaways
- Static rendering pre-builds pages for instant CDN delivery; dynamic renders per request.
- Next.js chooses automatically: static if no request-specific data; dynamic if
cookies(),headers(), orsearchParamsare accessed. - Incremental Static Regeneration (ISR) pre-builds pages but regenerates them on a schedule for automatic freshness.
- Hybrid applications combine static (marketing), ISR (content), and dynamic (user-specific) per-page.
- Choose rendering mode based on data freshness requirements and user-specificity.
Frequently Asked Questions
Can I statically render a page that has dynamic content?
Yes, using ISR or on-demand revalidation. Static rendering doesn't mean content never changes; it means you control when it changes (on a schedule or on-demand) rather than per-request.
Does dynamic rendering mean the page is never cached?
No. A dynamic page can still be cached per-user. The fetch cache (Next.js' cache) still applies; what changes is that each unique user request causes a new page render. The fetch results are cached, but the page computation per request happens.
What is on-demand revalidation?
On-demand revalidation regenerates a page when you explicitly call revalidatePath() or revalidateTag() from an API route or server action. This happens outside the schedule, typically triggered by a content management system (CMS) when an editor publishes. More in article 8.
How do I know if a page is static or dynamic?
In production, check the page's x-nextjs-cache response header (set by Vercel). Or measure time: static is <50 ms; dynamic is 100–300 ms. In development, build and inspect the .next/server directory to see which pages have static .html files.
Can I change a page from static to dynamic after deployment?
Only with a new deployment. Once deployed, static pages remain static until you change the code (add a cookies() call, etc.) and redeploy.