Master Next.js Rendering: PPR, SSR, CSR, SSG & ISR Explained

By Abhishek Khare
cover-image

Master Next.js Rendering: PPR, SSR, CSR, SSG & ISR Explained

Next.js is one of the most powerful frameworks for building modern web applications. One of its standout features is its flexible rendering strategies, which allow developers to optimize performance, SEO, and user experience. In this blog, we’ll dive deep into PPR (Partial Prerendering), SSR (Server-Side Rendering), CSR (Client-Side Rendering), SSG (Static Site Generation), and ISR (Incremental Static Regeneration). By the end, you’ll know exactly when and how to use each strategy.


Why Rendering Strategies Matter

Choosing the right rendering strategy can make or break your app’s performance. Here’s a quick breakdown of what each method offers:

  • CSR: Great for dynamic, interactive apps (e.g., dashboards).
  • SSR: Perfect for SEO-friendly, real-time data (e.g., user profiles).
  • SSG: Ideal for static content (e.g., blogs, documentation).
  • ISR: Best for frequently updated content (e.g., product pages).
  • PPR: Combines static and dynamic rendering for hybrid apps (e.g., marketing sites).

1. Client-Side Rendering (CSR)

CSR is the traditional React approach where the browser fetches data and renders the UI after the page loads. It’s great for interactive dashboards but not ideal for SEO.

"use client";
import { useEffect, useState } from "react";

export default function CSRPage() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch("https://randomuser.me/api/")
      .then((res) => res.json())
      .then((data) => setUser(data.results[0]));
  }, []);

  return (
    <div>
      <h1>Client-Side Rendering</h1>
      {user ? <p>Name: {user.name.first}</p> : <p>Loading...</p>}
    </div>
  );
}

Use Case: Dashboards, admin panels, or apps where SEO isn’t critical.


2. Server-Side Rendering (SSR)

SSR generates HTML on the server for each request, making it perfect for real-time data and SEO.

export const dynamic = "force-dynamic"; // Disable caching

export default async function SSRPage() {
  const res = await fetch("https://randomuser.me/api/", { cache: "no-store" });
  const user = (await res.json()).results[0];

  return (
    <div>
      <h1>Server-Side Rendering</h1>
      <p>Name: {user.name.first}</p>
    </div>
  );
}

Use Case: User profiles, live feeds, or pages requiring fresh data on every request.


3. Static Site Generation (SSG)

SSG pre-renders pages at build time, delivering blazing-fast performance and excellent SEO.

export default async function SSGPage() {
  const res = await fetch("https://randomuser.me/api/");
  const user = (await res.json()).results[0];

  return (
    <div>
      <h1>Static Site Generation</h1>
      <p>Name: {user.name.first}</p>
    </div>
  );
}

Use Case: Blogs, documentation, or any content that doesn’t change frequently.


4. Incremental Static Regeneration (ISR)

ISR combines SSG with periodic updates, making it ideal for frequently updated content.

export const revalidate = 60; // Revalidate every 60 seconds

export default async function ISRPage() {
  const res = await fetch("https://randomuser.me/api/");
  const user = (await res.json()).results[0];

  return (
    <div>
      <h1>Incremental Static Regeneration</h1>
      <p>Name: {user.name.first}</p>
    </div>
  );
}

Use Case: Product pages, news articles, or apps requiring frequent updates.


5. Partial Prerendering (PPR)

PPR is Next.js 15’s experimental feature that combines static and dynamic rendering in the same route.

import { Suspense } from "react";

async function StaticComponent() {
  const res = await fetch("https://randomuser.me/api/");
  const user = (await res.json()).results[0];

  return (
    <div>
      <h2>Static Profile</h2>
      <p>Name: {user.name.first}</p>
    </div>
  );
}

async function DynamicComponent() {
  const res = await fetch("https://randomuser.me/api/", { cache: "no-store" });
  const user = (await res.json()).results[0];

  return (
    <div>
      <h2>Dynamic Details</h2>
      <p>Email: {user.email}</p>
    </div>
  );
}

export default function PPPage() {
  return (
    <div>
      <h1>Partial Prerendering</h1>
      <StaticComponent />
      <Suspense fallback={<p>Loading dynamic data...</p>}>
        <DynamicComponent />
      </Suspense>
    </div>
  );
}

Use Case: Marketing sites, hybrid apps, or pages requiring both static and dynamic content.


Real-World Use Cases

E-Commerce App

  • SSG: Product descriptions
  • ISR: Pricing/availability
  • SSR: Personalized recommendations

News Platform

  • ISR: Article pages (revalidate hourly)
  • SSR: Live election results
  • CSR: Comment section

Analytics Dashboard

  • SSR: Initial auth-protected load
  • CSR: Interactive charts
  • ISR: Weekly reports

Performance Comparison

| Strategy | FCP | TTI | SEO | Use Case | | -------- | ----- | ---- | --- | ----------------------------- | | CSR | 2.5s | 1.8s | ❌ | Dashboards, admin panels | | SSR | 1.2s | 1.5s | ✅ | Real-time data, user profiles | | SSG | 0.3s | 0.3s | ✅ | Blogs, documentation | | ISR | 0.3s | 0.3s | ✅ | Product pages, news articles | | PPR | 0.3s+ | 0.5s | ✅ | Marketing sites, hybrid apps |


Conclusion

Next.js offers a variety of rendering strategies to suit different use cases. Whether you need blazing-fast static sites (SSG), real-time updates (SSR), or a hybrid approach (PPR), there’s a solution for every scenario. By understanding these strategies, you can build faster, more efficient, and SEO-friendly web applications.

Pro Tip: Always test your app in both development and production modes, as caching behavior can differ.


Free APIs Used: JSONPlaceholder, RandomUser, CatFact


If you found this guide helpful, share it with your network and follow me for more in-depth tutorials on Next.js and web development! 🚀