· frameworks  · 7 min read

Server-Side Rendering vs. Static Site Generation: The Next.js Debate

A practical guide to choosing between SSR and SSG in Next.js. Learn trade-offs, performance/cost implications, caching patterns, ISR, Edge, and a clear decision flow for real-world apps.

A practical guide to choosing between SSR and SSG in Next.js. Learn trade-offs, performance/cost implications, caching patterns, ISR, Edge, and a clear decision flow for real-world apps.

Outcome first: after this article you’ll be able to make a clear, pragmatic decision between Server-Side Rendering (SSR) and Static Site Generation (SSG) in Next.js - and understand when to reach for Incremental Static Regeneration (ISR) or Edge rendering instead.

The short answer up front

  • Use SSG when pages can be prebuilt and served from a CDN for maximum speed, lowest cost, and best scale.
  • Use SSR when you must render fresh, per-request HTML because content is highly dynamic or personalized and cannot be cached.
  • Use ISR (a hybrid) when most content is static but occasional updates are required without full rebuilds.

Keep that triad in mind as we dig into the how, why, and real-world trade-offs.

Quick primer: What Next.js gives you

Next.js provides multiple data-fetching strategies out of the box:

  • getStaticProps (SSG) - build-time HTML generation. Docs
  • getServerSideProps (SSR) - request-time HTML generation. Docs
  • Incremental Static Regeneration (ISR) - update static pages after build via revalidation. Docs
  • Edge Functions / Edge Runtime - run SSR/handlers closer to users for lower latency. Docs

If you’re using the App Router, data fetching shapes are similar but expressed via async Server Components, route handlers, and new patterns - see the App Router docs for nuances: App Router data fetching.

Deep dive: How SSG and SSR actually behave

Static Site Generation (SSG)

  • When: HTML is generated at build time. Good for blogs, docs, marketing pages, product pages with stable data.
  • Cost/Performance: Built pages are served from a CDN - extremely fast and cheap at scale.
  • Freshness: Stale until you rebuild (unless you use ISR or on-demand revalidation).
  • Scalability: Near-perfect. Serving static files is cheap and horizontally trivial.

Basic example (Pages Router):

export async function getStaticProps() {
  const res = await fetch('https://api.example.com/products');
  return { props: { products: await res.json() } };
}

(See: getStaticProps docs: https://nextjs.org/docs/basic-features/data-fetching/get-static-props)

Server-Side Rendering (SSR)

  • When: HTML is generated per request on the server.
  • Cost/Performance: Slower than serving static files because each request triggers server work; costs scale with traffic and render time.
  • Freshness: Always fresh by default (unless you add caching layers).
  • Scalability: Requires autoscaling or an edge runtime to handle increased traffic; caching can help but adds complexity.

Basic example (Pages Router):

export async function getServerSideProps(context) {
  const res = await fetch(
    `https://api.example.com/data?user=${context.req.cookies.userId}`
  );
  return { props: { data: await res.json() } };
}

(See: getServerSideProps docs: https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props)

Pros and cons at a glance

SSG (Pros)

  • Ultra-fast for end users via CDN.
  • Low server cost and great scale.
  • Simple caching, fewer moving parts.

SSG (Cons)

  • Build times grow with content size.
  • Not suitable for per-request personalization without client-side hydration or edge tricks.
  • Requires rebuilds or ISR to update content.

SSR (Pros)

  • Fresh data every request; good for personalization and rapidly-changing content.
  • Simpler authoring when server-only logic is required.

SSR (Cons)

  • Higher latency and cost per request.
  • Complexity: caching, concurrency, cold starts (serverless), and scaling must be managed.

Real-world trade-offs: speed, cost, and complexity

  • Speed: SSG wins for Time to First Byte (TTFB) and first-contentful paint because the CDN does most of the work. SSR introduces server render time into TTFB.
  • Cost: SSG usually costs less at scale because the CDN handles traffic. SSR costs grow proportionally with requests rendered.
  • Complexity: SSR often needs caching strategies (HTTP caching, CDN caching rules, stale-while-revalidate, surrogate keys), whereas SSG reduces the need for such infrastructure.

If you need sub-100ms TTFB worldwide, static pages on a CDN (SSG) are the easiest path.

When to choose SSR (concrete cases)

  • Highly personalized pages where HTML must reflect the current user (e.g., authenticated dashboards).
  • Content that changes on every request or depends on request headers/cookies.
  • When SEO-critical content must be accurate at request time and cannot be fetched by client-side JS.

If only a portion of the page needs personalization, consider SSG for the shell plus client-side fetches or edge middleware to selectively render.

When to choose SSG (concrete cases)

  • Marketing pages, documentation, blog posts, help centers, and product listing pages where content is stable between updates.
  • When you want predictable build-time validation and performance.
  • Projects that need to serve millions of reads cheaply.

If you need occasional updates, pair SSG with ISR or on-demand revalidation.

The hybrid answer: Incremental Static Regeneration (ISR)

ISR lets you have the best of both worlds: the CDN performance of static files with the ability to refresh pages after they’re built.

Example:

export async function getStaticProps() {
  const res = await fetch('https://api.example.com/posts');
  return {
    props: { posts: await res.json() },
    revalidate: 60, // seconds - Next.js will re-generate the page at most once per minute
  };
}
  • Revalidate > 0: Next.js serves the existing static HTML and triggers a background re-generation after the revalidate window. ISR docs
  • On-demand revalidation: You can programmatically revalidate specific pages from a webhook or server endpoint when content changes. On-demand revalidation

ISR is often the best first choice for content sites that need frequent updates without paying the cost of SSR.

Edge rendering and the App Router: a modern twist

Edge functions let you run SSR (or parts of it) close to users. This reduces latency compared to a central server and can be cheaper than traditional SSR if the runtime fits your code.

  • Use Edge when latency matters and your logic is compatible with the Edge Runtime (no heavy native modules, limited runtime APIs). Edge Runtime docs
  • The App Router introduces streaming SSR and React Server Components, enabling more granular control. See the App Router docs: App Router

Edge SSR combined with smart caching rules can approximate static-like performance while keeping per-request freshness.

Caching strategies you should know

  • Browser HTTP caching: set Cache-Control headers for both SSG and SSR responses.
  • CDN caching: with SSR responses, configure CDN to cache HTML with short TTLs and stale-while-revalidate for performance.
  • Surrogate keys / purge: use for targeted invalidation when a content update should purge related cached responses.

A recommended pattern: SSG for baseline content + ISR for periodic updates + client-side fetches for ultra-fresh user-specific bits.

Decision flow (practical, two-minute checklist)

  1. Does the page need to show different HTML per user or per request (cookies/headers)?
    • Yes → SSR or Edge SSR.
    • No → go to 2.
  2. Is the content reasonably stable and/or suitable for pre-rendering?
    • Yes → SSG (with ISR if updates are frequent).
    • No → SSR.
  3. Do you need global low-latency and can the code run on the Edge Runtime?
    • Yes → consider Edge rendering.
  4. Is cost or scale a concern (high read volume)?
    • Yes → prefer SSG/ISR and minimize SSR.

Examples: common page types and suggested approach

  • Blog post: SSG + ISR (revalidate on publish).
  • Marketing pages: SSG.
  • Product listing (large catalog): SSG with pagination + ISR or on-demand revalidation for source-of-truth updates.
  • Checkout flow / user dashboard: SSR or client-side rendering after an authenticated shell.
  • Search results: Client-side rendering or SSR depending on SEO needs and complexity.

Pitfalls and gotchas

  • Build times: huge SSG builds can be slow. Use incremental builds or filter what you pre-render.
  • Cold starts: serverless SSR can face cold starts - use warmers or edge runtimes when low latency is required.
  • Over-personalization: rendering unique HTML for every user destroys cache efficiency.
  • Misconfigured caching: SSR without proper CDN caching makes things expensive and slow.

Quick code snippets for reference

SSG with ISR (Pages Router):

export async function getStaticProps() {
  const res = await fetch('https://api.example.com/posts');
  return { props: { posts: await res.json() }, revalidate: 60 };
}

SSR (Pages Router):

export async function getServerSideProps(context) {
  // runs on every request
  const res = await fetch('https://api.example.com/user-data', {
    headers: { cookie: context.req.headers.cookie },
  });
  return { props: { user: await res.json() } };
}

App Router (Server Component pattern) example (conceptual):

// app/dashboard/page.server.jsx
export default async function DashboardPage() {
  const data = await fetch('https://api.example.com/dashboard', {
    cache: 'no-store',
  }).then(r => r.json());
  return <Dashboard data={data} />; // server-rendered per request
}

(See App Router data fetching patterns: https://nextjs.org/docs/app/building-your-application/data-fetching)

Final checklist before you decide

  • Measure expected read vs write traffic.
  • Identify per-page personalization needs.
  • Evaluate build times and content update cadence.
  • Consider hosting constraints: serverless versus edge.
  • Prototype both approaches for a critical page and measure real-world TTFB and cost.

Bottom line

SSG is the default win for performance, cost, and scale when pages can be prebuilt. SSR is the right tool when request-time freshness or personalization is mandatory. ISR and Edge renderers let you mix those benefits, but they add operational nuance.

In most modern Next.js apps, start with SSG (plus ISR where necessary). Reach for SSR only when the business requirement forces you to - static-first will save you latency, complexity, and money.

References

Back to Blog

Related Posts

View All Posts »