Next.js lets you control when and where rendering happens.
1. Client-Side Rendering (CSR) — browser cooks everything
This is the classic React behavior.
You visit /products, and the browser gets an empty shell first. Then JavaScript runs, makes a request, and fills the data.
"use client"
import { useEffect, useState } from "react"
export default function ProductsPage() {
const [products, setProducts] = useState([])
useEffect(() => {
fetch("/api/products")
.then(res => res.json())
.then(setProducts)
}, [])
return (
<>
<h1>New Products</h1>
{products.map(p => <div key={p.id}>{p.name}</div>)}
</>
)
}
Timeline of reality:
- Browser loads page
- Page is empty or loading
- JS runs
- JS fetches data
- UI updates
User waits longer. This is slower.
When to use:
- User dashboards
- Interactive UI
- Forms
- User-specific content
- When SEO is not important
When NOT to use:
- Public pages
- Product listings
- Blogs
- SEO pages
2. Server-Side Rendering (SSR) — server cooks on request
The browser requests /products.
The server immediately fetches products and sends fully prepared HTML.
export default async function ProductsPage() {
const res = await fetch("https://api.example.com/products", {
cache: "no-store"
})
const products = await res.json()
return (
<>
<h1>New Products</h1>
{products.map(p => <div key={p.id}>{p.name}</div>)}
</>
)
}
Timeline:
- Browser requests page
- Server fetches products
- Server builds HTML with products already inside
- Browser receives ready page instantly
Nothing loads afterward.
SSR = fresh data every request.
When to use:
- Live inventory
- Stock prices
- User-specific pages
- Data that must always be fresh
When NOT to use:
- Static content
- Blogs
- Marketing pages
- Data that rarely changes
3. Static Site Generation (SSG) — server cooks once at build time
Next.js fetches data during build and freezes it.
export default async function ProductsPage() {
const res = await fetch("https://api.example.com/products", {
cache: "force-cache"
})
const products = await res.json()
return (
<>
<h1>Popular Products</h1>
{products.map(p => <div key={p.id}>{p.name}</div>)}
</>
)
}
Timeline:
- You run
npm run build - Next.js fetches products
- Next.js saves ready HTML
- Every visitor gets that same HTML instantly
Fastest rendering method.
When to use:
- Blogs
- Landing pages
- Documentation
- Marketing pages
- Content that rarely changes
When NOT to use:
- Live dashboards
- Frequently changing data
- User-specific data
4. Incremental Static Regeneration (ISR) — server cooks occasionally
Next.js builds the page and updates it later automatically.
export default async function ProductsPage() {
const res = await fetch("https://api.example.com/products", {
next: { revalidate: 60 }
})
const products = await res.json()
return (
<>
<h1>Products</h1>
{products.map(p => <div key={p.id}>{p.name}</div>)}
</>
)
}
Timeline:
Build time:
- Products fetched
- Page saved
User visits within 60 seconds:
- Gets cached version instantly
After 60 seconds:
- First visitor still gets old version
- Next.js refreshes in background
- Future visitors get updated version
When to use:
- Product listings
- News sites
- Ecommerce pages
- Content that changes sometimes
When NOT to use:
- Real-time data
- User-specific private data
The Real Control Switch
cache: "no-store" → SSR
cache: "force-cache" → SSG
next: { revalidate: 60 } → ISR
"use client" → CSR (if fetching in browser)
"use client" controls WHERE code runs.
Not WHEN data is fetched.
"use client" → browser
no directive → server
Performance Order (Fastest to Slowest)
- SSG
- ISR
- SSR
- CSR
Work done earlier is always faster than work done later.
Final Mental Model
CSR → browser fetches after load
SSR → server fetches on every request
SSG → server fetches at build time
ISR → server fetches at build time and refreshes later
"use client" → browser execution
no "use client" → server execution
Top comments (0)