The 5 rendering patterns in Next.js

LinkIcon1. Client-Side Rendering (CSR)

LinkIconWhat it does:

  • The server sends the Javascript bundle to the client (React code).
  • The HTML is rendered in the browser after the JavaScript bundle loads.
  • Data is fetched on the client (browser) using useEffect, fetch(), or SWR, etc.
  • Initial page load shows a loading state (e.g., spinner) until data arrives.

Example:

'use client'
 
import { useEffect, useState } from 'react'
 
export default function Page() {
  const [data, setData] = useState(null)
 
  useEffect(() => {
    fetch('/api/data').then(res => res.json()).then(setData)
  }, [])
 
  return <div>{data ? data.title : 'Loading...'}</div>
}

LinkIconUse when:

  • SEO is not a priority (e.g. dashboards, authenticated views).
  • The data is user-specific or volatile, changing per session.

LinkIconDrawbacks:

  • Slower first paint.
  • Content is invisible to crawlers by default (bad for SEO).

LinkIcon2. Server-Side Rendering (SSR)

LinkIconWhat it does:

  • HTML is rendered on the server with fresh data on each request.
  • The client receives fully rendered HTML + JS hydration.

Example:

export default async function Page() {
  const data = await fetch('https://api.example.com/data', { cache: 'no-store' }).then(res => res.json)
 
  return <div>{data.title}</div>
}

LinkIconUse when:

  • SEO is important
  • You need fresh data on every request (e.g., auth-protected pages, dashboards).

LinkIconDrawbacks:

  • Slower performance (every request hits the server).
  • Caching needs to be managed manually.

LinkIcon3. Static Site Generation (SSG)

LinkIconWhat it does:

  • Data and HTML is generated at build time and served statically.
  • Runs once during build.
  • Serves the same HTML to every user.

Example:

export default async function Page() {
  const res = await fetch('https://api.example.com/data', { cache: 'force-cache' })
  const data = await res.json()
 
  return <div>{data.title}</div>
}

Good to know:

Next.js 15 default is cache: no-store. To enable SSG set cache: 'force-cache' :

LinkIconUse when:

  • Data is mostly static (e.g. documentation, blogs, marketing pages).
  • You want blazing-fast performance and great SEO.

LinkIconDrawbacks:

  • Outdated content if the data changes after build.
  • Long builds if you have many pages.

LinkIcon4. Incremental Static Regeneration (ISR)

LinkIconWhat it does:

  • Combines SSG speed with dynamic content.
  • Static pages are generated at build time then deployed.
  • After revalidate interval, static pages are rebuilt in the background on the next request.

Example:

export const revalidate = 60 // Revalidate every 60 seconds
 
export default async function Page() {
  const res = await fetch('https://api.example.com/data')
  const data = await res.json()
 
  return <div>{data.title}</div>
}

LinkIconUse when:

  • You want static performance with fresh data.
  • Content updates periodically (e.g., blog index, products).

LinkIconDrawbacks:

  • Slightly stale content in the revalidation window.
  • Not suited for highly dynamic or user-specific content.

LinkIcon5. Partial Pre-Rendering (PPR)

Available in Next.js 14 and this feature will be integrated in Next.js 16

LinkIconWhat it does:

  • Hybrid where parts of the page are statically generated, and other parts are rendered on the server at request time.
  • Creates a static shell for dynamic content using <Suspense>, and streams dynamic content as they're ready.
  • Static shell is sent instantly
  • Dynamic parts streamed in when ready

Example:

// /app/page.tsx
import { Suspense } from 'react'
 
export default function Page() {
  return (
    <div>
      <StaticHeader />
      <Suspense fallback={<SkeletonContent />}>
        <DynamicContent />
      </Suspense>
    </div>
  )
}
 
// DynamicContent.tsx (marked async)
export default async function DynamicContent() {
  const res = await fetch('https://api.example.com/data', { cache: 'no-store' })
  const data = await res.json()
  return <div>{data.title}</div>
}

LinkIconUse when:

  • You want fast static initial paint, but some components need live data (e.g., cart, auth).
  • Want to combine static + dynamic on one page without SSR.

LinkIconDrawbacks:

  • Still evolving, requires understanding of streaming + suspense.
  • Fine-tuning fallback UI is necessary for great UX.

LinkIconKey Differences Summary

FeatureGeneratedWhere RenderedUpdatesGood For
CSRIn browserClientEvery timeDashboards, UIs needing interactivity
SSRPer requestServerEvery requestAuth, dynamic data, personalization
SSGAt buildServer → StaticNeverBlog, docs, static landing pages
ISRAt buildServer → StaticOn revalidate intervalCatalogs, semi-dynamic
PPRStatic + streamMixedStream dynamic partsHybrid pages needing both SEO and personalization