The Ultimate Guide to Rendering in Next.js: SSR, SSG, ISR, CSR, PPR & Cache Explained

Introduction
If you've been building with React or Next.js, you've probably heard terms like:
SSR
SSG
ISR
CSR
PPR
React Server Components
unstable_cache
At first, they all sound like different ways of saying:
"Render a webpage."
But they actually solve very different problems.
After building several production Next.js applications, I realized that choosing the wrong rendering strategy can make your application slower, more expensive, or harder to maintain.
This guide explains each rendering method from a practical perspective, when to use it, and how they all fit together.
First: React vs Next.js
Before diving into rendering strategies, it's important to understand the difference.
React
React is only a UI library.
By itself, React renders everything in the browser.
Browser
β
Downloads JavaScript
β
Runs React
β
Page Appears
This is called Client-Side Rendering (CSR).
Next.js
Next.js extends React by allowing pages to render:
On the server
During build time
Incrementally
Partially
Or entirely on the client
That's why Next.js offers multiple rendering strategies.
1. Client-Side Rendering (CSR)
User
β
HTML Shell
β
Download JS
β
Fetch API
β
Render UI
Everything happens in the browser.
Example:
'use client'
const { data } = useSWR('/api/posts')
Pros
Great interactivity
Perfect for dashboards
Minimal server work
Cons
Slower first load
Poor SEO
Users wait for JavaScript
Best For
Admin panels
Internal dashboards
Authenticated apps
Highly interactive pages
2. Server-Side Rendering (SSR)
Instead of sending an empty page...
Next.js renders everything on the server for every request.
User
β
Server
β
Database
β
HTML
β
Browser
Example:
export default async function Page() {
const posts = await getPosts()
return <Posts posts={posts} />
}
Every refresh executes the server code again.
Pros
Fresh data
Excellent SEO
Faster first paint
Cons
Higher server cost
Slower response if database is slow
Best For
News
Search results
User profiles
Personalized pages
3. Static Site Generation (SSG)
Instead of rendering when users visit...
Pages are generated during build time.
Build
β
Generate HTML
β
Deploy
β
Serve Static File
No server code runs when users visit.
Example:
npm run build
creates
about.html
pricing.html
contact.html
Pros
Extremely fast
Zero database queries
Cheap hosting
Cons
Content becomes outdated
Requires rebuilding
Best For
Landing pages
Documentation
Blogs
Marketing sites
4. Incremental Static Regeneration (ISR)
ISR combines SSG with automatic regeneration.
User
β
Cached Page
β
TTL Expires
β
Background Regeneration
Example:
export const revalidate = 60
Meaning:
Generate once
β
Cache 60 seconds
β
Regenerate automatically
Users always receive a cached page while regeneration happens in the background.
Pros
Almost static performance
Fresh content
Lower server costs
Best For
Ecommerce
Product pages
Restaurant menus
CMS websites
5. Partial Prerendering (PPR)
One of the newest rendering strategies in Next.js.
Instead of choosing between SSR and SSG...
You can mix both.
Static Header
β
Static Hero
β
Static Footer
β
Dynamic Products
β
Dynamic Notifications
Static parts are served immediately.
Dynamic sections stream in later.
Benefits
Faster loading
Better Core Web Vitals
Smaller TTFB
Great UX
Best For
Large pages with only a few dynamic sections.
React Server Components (RSC)
Server Components aren't a rendering strategy.
They're a React feature.
They:
Run only on the server
Never ship JavaScript
Can access databases directly
Example:
export default async function Products() {
const products = await db.product.findMany()
return <ProductList products={products} />
}
No API route needed.
Where Does unstable_cache Fit?
This is probably the most misunderstood feature.
It does not render pages.
Instead...
It caches expensive server functions.
Without cache:
Request
β
Database
β
Response
β
Database Again
β
Response
With cache:
Request
β
Cache
β
Response
Example:
const getRestaurants = unstable_cache(
async () => prisma.restaurant.findMany(),
['restaurants'],
{
tags: ['restaurants']
}
)
Now the database isn't queried every request.
Instead, Next.js serves cached results.
Cache Invalidation
Instead of waiting for time...
You can invalidate cache immediately.
revalidateTag('restaurants')
Perfect after:
Creating a restaurant
Updating a menu
Deleting content
How Everything Fits Together
React
β
Client Rendering
β
Next.js
β
βββββββββββββββββββββββββββββββββββ
β CSR β SSR β SSG β ISR β PPR β
βββββββββββββββββββββββββββββββββββ
β
React Server Components
β
unstable_cache
β
Database / APIs
Which Framework Uses What?
| Framework | CSR | SSR | SSG | ISR | PPR |
|---|---|---|---|---|---|
| React | β | β | β | β | β |
| Next.js | β | β | β | β | β |
| Remix | β | β | Limited | β | β |
| Astro | Partial | β | β | Partial | β |
| Nuxt | β | β | β | Partial | β |
| SvelteKit | β | β | β | Partial | β |
Which One Should You Use?
| Scenario | Recommended |
|---|---|
| Admin Dashboard | CSR |
| Marketing Website | SSG |
| Blog | ISR |
| News Website | SSR |
| Ecommerce | ISR |
| User Profile | SSR |
| Documentation | SSG |
| Restaurant Menu | ISR |
| SaaS Dashboard | CSR |
| Hybrid Homepage | PPR |
Common Mistakes
β Using SSR for everything
β Using CSR for SEO pages
β Rebuilding for every CMS update
β Forgetting cache invalidation
β Calling the database repeatedly instead of caching
Where Does SWR Fit?
One thing that confused me when learning Next.js was SWR.
At first, I thought it was another rendering strategy like SSR or ISR.
It isn't.
SWR is a client-side data fetching library created by Vercel.
Think of it as a smart cache for API requests inside the browser.
Example:
'use client'
import useSWR from 'swr'
const fetcher = (url: string) => fetch(url).then(res => res.json())
export default function Users() {
const { data, error, isLoading } = useSWR('/api/users', fetcher)
if (isLoading) return <>Loading...</>
return <div>{data.name}</div>
}
Instead of manually doing:
useEffect(() => {
fetch(...)
}, [])
SWR automatically handles:
β Client-side caching
β Request deduplication
β Background revalidation
β Automatic refetch on window focus
β Retry on network failure
β Optimistic UI updates
SWR vs unstable_cache
This is how I remember it:
| Feature | unstable_cache |
SWR |
|---|---|---|
| Runs on | Server | Browser |
| Purpose | Cache expensive database/API functions | Cache API requests on the client |
| Used for | Prisma queries, external APIs, expensive computations | Dashboards, user profile, live UI |
| Invalidated by | revalidateTag() / revalidatePath() |
mutate() |
| Part of rendering? | β No | β No |
A Simple Mental Model
Whenever I forget where each fits, I remember it like this:
Database
β
βΌ
unstable_cache
(Server Cache)
β
βΌ
SSR / ISR / SSG / PPR
(Render HTML)
β
βΌ
Browser
β
βΌ
SWR
(Client Cache)
β
βΌ
User Interface
Rule of Thumb
SSR β Render fresh HTML on every request.
SSG β Render once during build.
ISR β Static pages that automatically regenerate.
CSR β Render everything in the browser.
PPR β Mix static and dynamic rendering.
React Server Components β Server-only React components.
unstable_cacheβ Cache expensive server functions.SWR β Cache and revalidate API requests in the browser.
Final Thoughts
There isn't a "best" rendering strategy.
Each solves a different problem.
A modern Next.js application often combines several of them:
SSG for static pages
ISR for content updates
SSR for personalized experiences
CSR for interactive dashboards
PPR for hybrid performance
React Server Components for efficient server logic
unstable_cacheto reduce database load
Understanding when to use each is what separates a working application from a fast, scalable one.
Happy building! π
Useful Resources
Next.js Rendering Docs: https://nextjs.org/docs/app/building-your-application/rendering
React Server Components: https://react.dev/reference/rsc/server-components
Next.js Caching Guide: https://nextjs.org/docs/app/guides/caching
unstable_cache API: https://nextjs.org/docs/app/api-reference/functions/unstable\_cache





