The 5 rendering patterns in Next.js
1. Client-Side Rendering (CSR)
What 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()
, orSWR
, 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>
}
Use when:
- SEO is not a priority (e.g. dashboards, authenticated views).
- The data is user-specific or volatile, changing per session.
Drawbacks:
- Slower first paint.
- Content is invisible to crawlers by default (bad for SEO).
2. Server-Side Rendering (SSR)
What 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>
}
Use when:
- SEO is important
- You need fresh data on every request (e.g., auth-protected pages, dashboards).
Drawbacks:
- Slower performance (every request hits the server).
- Caching needs to be managed manually.
3. Static Site Generation (SSG)
What 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'
:
Use when:
- Data is mostly static (e.g. documentation, blogs, marketing pages).
- You want blazing-fast performance and great SEO.
Drawbacks:
- Outdated content if the data changes after build.
- Long builds if you have many pages.
4. Incremental Static Regeneration (ISR)
What 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>
}
Use when:
- You want static performance with fresh data.
- Content updates periodically (e.g., blog index, products).
Drawbacks:
- Slightly stale content in the revalidation window.
- Not suited for highly dynamic or user-specific content.
5. Partial Pre-Rendering (PPR)
Available in Next.js 14 and this feature will be integrated in Next.js 16
What 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>
}
Use 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.
Drawbacks:
- Still evolving, requires understanding of streaming + suspense.
- Fine-tuning fallback UI is necessary for great UX.
Key Differences Summary
Feature | Generated | Where Rendered | Updates | Good For |
---|---|---|---|---|
CSR | In browser | Client | Every time | Dashboards, UIs needing interactivity |
SSR | Per request | Server | Every request | Auth, dynamic data, personalization |
SSG | At build | Server → Static | Never | Blog, docs, static landing pages |
ISR | At build | Server → Static | On revalidate interval | Catalogs, semi-dynamic |
PPR | Static + stream | Mixed | Stream dynamic parts | Hybrid pages needing both SEO and personalization |