Skip to main content

Command Palette

Search for a command to run...

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

Updated
β€’8 min readβ€’View as Markdown
The Ultimate Guide to Rendering in Next.js: SSR, SSG, ISR, CSR, PPR & Cache Explained
A
πŸ€– Curious AI enthusiast and Software Engineer with 7+ years of experience. πŸ› οΈ I enjoy getting my hands dirty with new technologies β€” experimenting, building side projects, and learning by doing. πŸš€ Currently exploring how AI can enhance real-world web applications.

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_cache to reduce database load

Understanding when to use each is what separates a working application from a fast, scalable one.

Happy building! πŸš€


Useful Resources