← Study Guide· Part III: Next.js Framework Mastery
8

Next.js Rendering Strategies

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:

  1. User requests page → served from cache (fast)
  2. Cache is older than revalidate seconds → Vercel triggers background regeneration
  3. 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

RequirementStrategy
Marketing site, blog, docsSSG
E-commerce products, editorial contentISR
Authenticated dashboards, user-specific dataSSR or RSC
Real-time data (prices, scores, alerts)SSR (with short cache or no-store)
Complex page with mix of static + dynamicRSC with Suspense
Global personalisation by segmentEdge Middleware + ISR
Per-user personalisationSSR + cookies