Technology

Server-Side Rendering vs Static Generation: Trade-Offs

When to use SSR, SSG, ISR, and hybrid approaches for optimal performance and maintainability.

All articles
TechnologyNexaEx TeamMarch 15, 2026 8 min read
Server-Side Rendering vs Static Generation: Trade-Offs

SSR vs SSG: Choosing the Right Rendering Strategy

Each rendering strategy—SSR, SSG, ISR—has trade-offs. The right choice depends on content freshness, scale, and budget.

Static Generation (SSG)

Pre-build pages at deploy time. Pages are static HTML served from CDN.

// Next.js
export async function getStaticProps() {
  const posts = await fetch('https://api.example.com/posts')
    .then(r => r.json())

  return {
    props: { posts },
    revalidate: 3600, // ISR: revalidate every hour
  }
}

Advantages:

  • Speed: Serve from CDN, minimal latency.
  • Scale: Millions of concurrent users; CDN handles load.
  • Cost: Cheap—no servers needed.
  • SEO: Full HTML at request time; crawlers happy.

Disadvantages:

  • Build time: Rebuilding 1M pages takes hours.
  • Freshness: Content stale until rebuild.
  • Dynamic content: Hard to personalize per-user.

Best for: Blogs, marketing sites, documentation. Content changes infrequently.

Server-Side Rendering (SSR)

Render pages on-demand on the server.

// Next.js
export async function getServerSideProps() {
  const posts = await fetch('https://api.example.com/posts')
    .then(r => r.json())

  return { props: { posts } }
}

Advantages:

  • Freshness: Always current.
  • Dynamic content: Easy to personalize per-user.
  • Flexibility: No build step constraints.

Disadvantages:

  • Latency: Server must render every request (100-500ms overhead).
  • Cost: Server infrastructure required.
  • Scale: Hard to scale beyond a few requests/second without caching.

Best for: User dashboards, real-time data, personalized content.

Incremental Static Regeneration (ISR)

Hybrid: generate static pages, revalidate on-demand.

export async function getStaticProps() {
  const posts = await fetch('https://api.example.com/posts')
    .then(r => r.json())

  return {
    props: { posts },
    revalidate: 60, // Revalidate every 60 seconds
  }
}

How it works:

  1. User requests /blog/post-1.
  2. Server checks: Is the cached version older than 60 seconds?
  3. If fresh, serve cached version instantly.
  4. If stale, regenerate in background, serve old version immediately.
  5. Next user gets fresh version.

Advantages:

  • Best of both: CDN speed + fresh content.
  • On-demand revalidation: Webhook from CMS → trigger revalidate → next user sees fresh content.
  • Scales well: Cache hits are fast; regeneration happens in background.

Disadvantages:

  • More complex to set up.
  • Requires understanding cache semantics.

Best for: Blogs, news sites, product catalogs. Content changes occasionally.

Dynamic Routes with SSG

Large sites with millions of dynamic routes:

// Generate ~1000 most popular pages at build time
export async function getStaticPaths() {
  const topPosts = await fetch('https://api.example.com/posts/top?limit=1000')
    .then(r => r.json())

  return {
    paths: topPosts.map(p => ({ params: { slug: p.slug } })),
    fallback: 'blocking', // Others render on-demand (SSR)
  }
}

fallback options:

  • false: Only pre-generated paths work. 404 for others.
  • 'blocking': Pre-generate on-demand, then cache. User waits.
  • true: Serve stale version immediately, regenerate in background.

Comparison Table

FeatureSSGSSRISR
SpeedFastestMediumFast
FreshnessStaleAlways freshFresh within TTL
Build timeSlowNo buildMedium
CostCheapestExpensiveCheap
ScalabilityUnlimitedLimitedHigh
ComplexityLowLowMedium

Practical Strategy

Marketing site: SSG. Content rarely changes. Build once, serve forever.

Blog: ISR. New posts a few times a week. Revalidate on publish.

SaaS dashboard: SSR. All content is user-specific. Cache aggressively server-side.

E-commerce: Hybrid. Product listing pages use ISR. User cart/checkout use SSR.

Caching Headers

Combine rendering strategies with HTTP caching:

// SSG page: Cache forever
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')

// SSR page: Cache for 60 seconds
res.setHeader('Cache-Control', 'public, max-age=60')

// Private (user-specific): Don't cache
res.setHeader('Cache-Control', 'private, no-cache')

ISR In Practice

CMS publishes a blog post → webhook to /api/revalidate?secret=token&slug=new-post → Next.js revalidates /blog/new-post → next request gets fresh version.

// pages/api/revalidate.js
export default async function handler(req, res) {
  if (req.query.secret !== process.env.REVALIDATE_SECRET) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  await res.revalidate(`/blog/${req.query.slug}`)
  res.json({ revalidated: true })
}

Simple, powerful, and scales incredibly well.

Frequently asked questions

Which is fastest: SSG or SSR?

SSG. Pre-built pages served from CDN are instantaneous. SSR requires server render time (100-500ms). ISR combines both.

Can I use ISR for pages with user-specific content?

No. ISR caches pages globally. User-specific content needs SSR or client-side fetch.

How long should I set revalidate timeout for ISR?

Depends on content freshness needs. Blog: 3600s (1 hour). News: 60s. Fast-changing: 5-10s. Balance freshness vs. cache hit rate.

Let's build your next idea

One conversation to scope the work, meet the team, and get a proposal — usually within two business days.