Skip to content

Next.js Rendering Strategies

This lesson steps back from individual strategies to give you a single, comparative view of every rendering option Next.js offers, and a practical framework for choosing the right one per route.

Four Rendering Strategies, One Framework

Next.js doesn't force your whole app into one rendering strategy — every route independently ends up static, dynamic (SSR), or ISR based on the data-fetching choices made inside it, and any route can additionally use Client Components for purely client-rendered widgets.

Understanding these strategies as a spectrum, rather than four unrelated concepts, makes it much easier to reason about a real page that mixes them — for example, a mostly-static product page (SSG) with a small, client-rendered "recently viewed" widget (CSR).

// Static (default, no dynamic data)
export default function AboutPage() { return <h1>About</h1>; }

// ISR (time-based revalidation)
export const revalidate = 60;

// SSR (dynamic, per-request)
export const dynamic = "force-dynamic";

// CSR (inside a Client Component)
"use client";
useEffect(() => { /* fetch on the client */ }, []);

All four strategies can coexist across different routes — and even within different components on the same page.

The Rendering Spectrum

Static (SSG)  →  ISR  →  SSR (dynamic)  →  CSR (client-side)
Rendered once     Rendered      Rendered every    Rendered in
at build time     periodically  request            the browser
  • Static rendering: fastest, cheapest, but content is fixed until the next build.
  • ISR: static performance with periodic (or on-demand) content updates.
  • SSR: always fresh, at the cost of per-request server work.
  • CSR: rendered entirely in the browser, useful for highly interactive, non-SEO-critical UI.

Rendering Strategy Cheat Sheet

A decision guide for choosing a rendering strategy.

Strategy Choose When
Static (SSG) Content is the same for everyone and rarely changes
ISR Content changes occasionally; some staleness is acceptable
SSR Content is personalized or must be current on every request
CSR UI is highly interactive and doesn't need to be indexed by search engines

Mixing Strategies on a Single Page

A single route's rendering mode (static, ISR, or SSR) is determined by its Server Components, but that same page can still include Client Components that fetch and render their own data entirely in the browser — effectively layering CSR on top of whatever the page's baseline strategy is.

  • Page shell (static or ISR) → fast initial load, good SEO.
  • Embedded Client Component (CSR) → live, personalized widget, like a shopping cart count.
  • This hybrid approach is common and often the ideal trade-off.

A Practical Default: Start Static, Add Dynamism Only Where Needed

A productive default strategy is to build every route as static first, then add exactly the dynamic behavior (cookies, no-store fetches, force-dynamic) required by that specific route's actual requirements — rather than defaulting every route to SSR out of caution.

Common Mistakes

  • Defaulting every route to force-dynamic without checking whether static rendering or ISR would work just as well.
  • Assuming a page must use one strategy uniformly, when Client Components can add CSR to any page.
  • Not measuring the actual performance/cost trade-off between strategies before optimizing prematurely.
  • Confusing traditional CSR (a fully client-rendered SPA) with a Client Component embedded in an otherwise server-rendered page.

Key Takeaways

  • Next.js supports static rendering, ISR, SSR, and CSR — often within the very same page.
  • Each route's strategy is determined by the data-fetching choices inside its Server Components.
  • Client Components layer CSR on top of a page's underlying server rendering strategy.
  • A good default is to start static and add dynamic behavior only where actually required.
  • Choosing the right strategy per route (not per app) is central to Next.js performance.

Pro Tip

When reviewing a slow Next.js page, check next build's route summary first to see whether it's actually being rendered the way you assume — many performance issues trace back to a route unintentionally being dynamic when it could have been static or ISR.