8. Next.js Rendering Strategies
This is the core technical topic for any Vercel SE. You must be able to explain all four strategies, know when to use each, and diagnose when a customer is using the wrong one.
The Four Strategies
8.1 Static Site Generation (SSG)
What it is: Pages rendered at build time. HTML is generated once, served from CDN everywhere.
How to implement:
// App Router (Next.js 13+)
// Default for pages without dynamic data
export default async function Page() {
const data = await fetch('https://api.example.com/data', {
cache: 'force-cache' // default — cached indefinitely
});
return <div>{data.content}</div>;
}
When to use:
- Marketing pages, landing pages, blog posts
- Content that changes rarely (hours to days)
- Maximum CDN cache hit rate required
- Best SEO performance (HTML pre-rendered)
Limitations:
- Requires full rebuild to update content
- Poor fit for personalised content
- Not suitable for real-time data
On Vercel advantage: Static assets are distributed across all CDN PoPs globally, served in milliseconds.
8.2 Incremental Static Regeneration (ISR)
What it is: Pages are statically generated, but automatically regenerated in the background when their cache expires — without a full site rebuild.
How to implement:
// Time-based revalidation
export default async function Page() {
const data = await fetch('https://api.example.com/products', {
next: { revalidate: 3600 } // regenerate after 1 hour
});
return <ProductList products={data} />;
}
// On-demand revalidation via webhook
import { revalidatePath } from 'next/cache';
export async function POST(request) {
const { slug } = await request.json();
revalidatePath(`/products/${slug}`);
return Response.json({ revalidated: true });
}
The stale-while-revalidate behaviour:
- User requests page → served from cache (fast)
- Cache is older than
revalidateseconds → Vercel triggers background regeneration - Next user gets the fresh version
Cache stampede problem (and Vercel's solution): When many users request the same ISR route simultaneously and the cache is expired, each request can trigger its own function invocation — overloading your backend. Vercel's CDN now prevents this with request collapsing: only one request per region invokes a function; the rest wait and get the cached response.
When to use:
- E-commerce product pages (update frequently but not every request)
- News sites, blogs with editorial updates
- Any content that updates on a schedule or via CMS publish events
- Large sites where full rebuilds are impractical (100k+ pages)
On Vercel advantage: Self-hosted ISR is limited to a single region and doesn't persist generated pages to durable storage. On Vercel, ISR pages are distributed globally across the CDN.
8.3 Server-Side Rendering (SSR)
What it is: Pages rendered on every request using a Vercel Function. Data is always fresh.
How to implement:
// Force dynamic rendering (no caching)
export const dynamic = 'force-dynamic';
export default async function Page({ searchParams }) {
const user = await auth(); // authentication — different per request
const data = await db.query(
`SELECT * FROM orders WHERE user_id = $1`,
[user.id]
);
return <Dashboard orders={data} />;
}
When to use:
- Authenticated pages (different content per user)
- Real-time data requirements (live stock prices, live scores)
- Pages that use request-specific data (cookies, headers, geo)
- Search results pages with user-specific filtering
Cost consideration: Every request invokes a Vercel Function. High-traffic SSR routes are expensive. Guide customers to use ISR where possible and SSR only where required.
8.4 React Server Components (RSC) — The Modern Approach
What it is: Components that render on the server but aren't SSR in the traditional sense. RSC allows selective server/client rendering at the component level, not the page level.
How it works:
// Server Component (default in App Router)
// Renders on server, zero client-side JS
async function ProductDetails({ id }) {
const product = await db.getProduct(id); // direct DB access, no API needed
return <div>{product.name}</div>;
}
// Client Component (for interactivity)
'use client';
function AddToCart({ productId }) {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>Add ({count})</button>;
}
Why RSC matters for customers:
- Zero JavaScript shipped for server components (improves LCP, reduces bundle)
- Direct database access without API layer
- Compose server and client components freely
- Works with Suspense for streaming partial UI
Streaming with Suspense:
import { Suspense } from 'react';
export default function Page() {
return (
<>
<Header /> {/* renders immediately */}
<Suspense fallback={<Skeleton />}>
<SlowDataComponent /> {/* streams in when ready */}
</Suspense>
</>
);
}
Rendering Strategy Decision Matrix
| Requirement | Strategy |
|---|---|
| Marketing site, blog, docs | SSG |
| E-commerce products, editorial content | ISR |
| Authenticated dashboards, user-specific data | SSR or RSC |
| Real-time data (prices, scores, alerts) | SSR (with short cache or no-store) |
| Complex page with mix of static + dynamic | RSC with Suspense |
| Global personalisation by segment | Edge Middleware + ISR |
| Per-user personalisation | SSR + cookies |