Next.js provides different rendering patterns like CSR, SSR, SSG, ISR, RSC, PPR, and DPR. In this article, we’ll go through what each means and when to use them.
Introduction:
Rendering is the process of turning code and data into a visual interface that users can interact with. In web development, the way a page is rendered plays a major role in overall performance, SEO, user experience, and scalability.
Next.js, one of the most popular React frameworks, provides several rendering strategies — including CSR (Client-Side Rendering), SSR (Server-Side Rendering), SSG (Static Site Generation), ISR (Incremental Static Regeneration), RSC (React Server Components), PPR (Partial Pre-rendering), and DPR (Dynamic Partial Rendering). Each of these techniques offers a different balance between speed, interactivity, and data freshness depending on the use case.
By understanding how these rendering methods work, developers can make informed decisions about which approach best fits their project — whether the goal is lightning-fast performance, improved SEO, or a more dynamic and interactive user experience.
Client-Side Rendering (CSR)
Definition:
In Client-Side Rendering (CSR), the server only sends a minimal HTML file with the required JavaScript bundles. Once the JavaScript is downloaded and executed in the browser, the client fetches the necessary data, hydrates the app, and renders the user interface (UI) dynamically. This makes the app very interactive, but it slows down the initial load and may negatively affect search engine optimization (SEO).
Use Case:
CSR is typically used, where user interaction is highly important and SEO is not a priority, such as:
- User and admin dashboards
- Chat and messaging applications
- Online design and editing tools
- SaaS platforms with dynamic data
- Progressive Web Apps (PWA)
Advantages & Disadvantages:
Advantages:
- High interactivity and smooth user experience
- Reduced processing load on the server
- Ability to update parts of the UI without reloading the entire page
Disadvantages:
- Slower initial load because that need to download and execute JavaScript
- Limitations in SEO
- Full dependency on JavaScript execution in the browser
Example:
To implement CSR in Next.js, you must utilize the "use client" directive at the top of a file. This tells the framework to render the component on the client side rather than on the server side.
As an illustration in the next section, a simple counter is initialized with useState. Its value is stored and modified only on the client side. On a user action's click event, state is updated and UI is re-rendered without a refresh of the page. Such a response is one in which "use client" is facilitating CSR.
"use client";
import { useState } from "react";
export default function CSRExample() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Client-Side Rendering Example</h1>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click Me
</button>
</div>
);
}
Server-Side Rendering (SSR)
Definition:
Server-Side Rendering (SSR) is a rendering strategy where the HTML content is generated on the server for every request before being sent to the client. This technique ensures better SEO, faster first meaningful paint, and enables dynamic data fetching at request time.
Use Case:
SSR is preferred for:
- SEO-critical websites
- E-commerce product pages
- News and media portals
- Content-heavy landing pages
Advantages & Disadvantages:
Advantages:
- Better SEO
- Faster first content display
- Dynamic data rendering
Disadvantages:
- Higher server load
- Slower navigation between pages
- Requires server for rendering
Example:
Each time the page is requested, the server generates fresh HTML content that includes the current server time before sending it to the client. This ensures that the user always sees up-to-date data rendered directly on the server.
export default async function SSRExample() {
const time = new Date().toLocaleTimeString();
return (
<div>
<h1>Server-Side Rendering Example</h1>
<p>Current server time: {time}</p>
</div>
);
}
Static-Site Generation (SSG)
Definition:
Static-Site Generation (SSG) pre-renders pages at build time (during next build) and serves the static HTML on every request. It provides excellent first-load performance, works well with CDNs, and offers strong SEO benefits.
Use Case:
SSG is preferred for:
- Blogs and documentation
- Marketing/landing pages
- Catalogs that don’t update often
- Content that benefits from global caching
Advantages & Disadvantages:
Advantages:
- fast initial load & SEO
- CDN-friendly & scalable
- Low server cost at runtime
Disadvantages:
- Stale content until rebuild or revalidate
- Build time grows with number of pages
- Requires strategy for dynamic data
Example:
dynamic = "force-static" → Forces the page to be generated once at build time and served as static HTML.
generateStaticParams() → Defines static routes or data that are precomputed during build.
The page is not regenerated on each request, making it very fast and CDN-friendly.
export const dynamic = "force-static"; // ensures SSG
export async function generateStaticParams() {
// simulate static data at build time
const posts = [{ id: 1 }, { id: 2 }];
return posts.map((p) => ({ id: p.id.toString() }));
}
export default function BlogList() {
return (
<div className="text-center">
<h1>Static Site Generation Example</h1>
<p>This page is generated at build time.</p>
</div>
);
}
Incremental-Static Regeneration (ISR)
Definition:
Incremental Static Regeneration (ISR) enables Next.js applications to serve pre-rendered static pages while also permitting updates in the background based on a revalidation interval or through explicit on-demand triggers. It combines the performance benefits of SSG with the freshness of dynamic content.
Use Case:
- Blogs, product catalogs, documentation, or marketing sites that need regular updates
- CMS-driven or event-triggered updates with on-demand revalidation
Advantages & Disadvantages:
Advantages:
- Fast static-first loads
- No full rebuild needed
- CDN & cache friendly
- On-demand freshness
Disadvantages:
- Slightly stale-first request after revalidate
- Background regeneration uses server resources
- Only regenerates when requested
- On-demand adds complexity
Example:
Combines SSG performance with the freshness of SSR.
The revalidate = 60 property means this page will be regenerated every 60 seconds in the background.
The next visitor gets updated content automatically.
export const revalidate = 60;
export default async function Posts() {
const posts = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5")
.then((r) => r.json());
return (
<div>
<h1>ISR Example</h1>
<ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>
</div>
);
}
React-Server Components (RSC)
Definition:
React Server Components (RSC) provide a mechanism to render components on the server without sending their JavaScript to the client. They reduce bundle size, improve performance, and enable secure server-side data access. Next.js App Router integrates RSC by default, supporting static, dynamic, and streaming server-rendering strategies.
Use Case:
RSC is ideal when:
- You need server-side data fetching with secrets, e.g., DB access.
- You want to reduce client JS bundle size.
- You require fast initial load and partial streaming
Advantages & Disadvantages:
Advantages:
- Reduced client bundle
- Secure server-side logic
- Efficient streaming & caching
Disadvantages:
- No client-side state/hooks in RSC
- Less familiar mental model
- Mixed boundary complexity
Example:
The main page (page.tsx) is a Server Component — it runs on the server and never sends JS to the client.
LikeButton is a Client Component since it uses React state.
This hybrid model minimizes client JS, speeds up initial load, and protects server-only data (like database queries).
// app/page.tsx — default Server Component
export default async function Page() {
const todos = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
).then((r) => r.json());
return <h1>{todos.title}</h1>;
}
// app/ui/LikeButton.tsx — Client Component
("use client");
import { useState } from "react";
export default function LikeButton({ initial }: { initial: number }) {
const [likes, setLikes] = useState(initial);
return <button onClick={() => setLikes(likes + 1)}>{likes}</button>;
}
Dynamic-Partial Rendering (DPR)
Definition:
Dynamic Partial Rendering (DPR) renders only the dynamic parts of a page on request, while other sections remain static or pre- rendered. This speeds up responses and avoids full SSR when it’s not needed.
Use Case:
- Large pages with mixed static and dynamic content.
- Dashboards or profiles with personalized content
- Applications where only specific parts of the page change per request.
Advantages & Disadvantages:
Advantages:
- Optimized performance by avoiding full SSR.
- Reduced server workload compared to rendering the entire page dynamically.
- Better handling of time-sensitive or user-specific data.
Disadvantages:
- More complex state management across static and dynamic areas
- Possible inconsistencies between static and dynamic sections
- Requires careful architectural design to prevent server overhead.
Partial Pre-Rendering (PPR)
Definition:
Partial Pre-Rendering (PPR) is a hybrid method where static parts of a page load immediately, while dynamic parts are progressively streamed and hydrated using React Suspense. This gives users instant content while waiting for updates.
Use Case:
- Pages that are mostly static but need a few live updates
- Blog posts or product pages with mostly stable info but dynamic details (like price/stock)
- SEO-heavy apps that still need real-time data updates
Advantages & Disadvantages:
Advantages:
- Improved user experience (UX) since static content appears instantly.
- Reduced Time to First Byte (TTFB).
- SEO-friendly because search engines can crawl the pre-rendered static parts.
Disadvantages:
- More complex to implement since it mixes static and dynamic rendering
- Requires correct use of React Suspense/streaming
- Can cause layout shifts if dynamic content loads later.
Example:
Static content appears instantly, while dynamic sections (like product price) stream in later.
React Suspense handles streaming and hydration seamlessly.
import { Suspense } from "react";
import ProductDetails from "./ProductDetails";
export default function ProductPage() {
return (
<div>
<h1>Partial Pre-Rendering Example</h1>
<Suspense fallback={<p>Loading price...</p>}>
<ProductDetails />
</Suspense>
</div>
);
}
Rendering Pattern Comparison:
Rendering Mode | Initial Perf | SEO Friendliness | Freshness | Complexity | JS Load | Typical Use Cases | Notes |
---|---|---|---|---|---|---|---|
CSR | Medium to poor initially; strong after hydration | Low to medium | High (client) | Low | High | Dashboards, SPA tools, interactive apps | Not SEO-friendly, good for highly interactive pages after login |
SSR | Good (higher TTFB) | High | Very high | Medium | Medium | Auth pages, live pricing, search pages | SEO-friendly, dynamic content, moderate JS load |
SSG | Excellent (CDN) | High | Low (without ISR) | Low | Low | Blogs, docs, marketing pages | Very fast, ideal for public static pages, limited frequent updates |
ISR | Near SSG with periodic/on-demand freshness | High | High (interval + on-demand) | Medium | Low | Catalogs, CMS-driven pages | Combines SSG speed with content updates, supports revalidation |
RSC | Excellent (less client JS, streaming) | High | Depends on strategy | Medium | Very low | Data-driven pages with minimal client-side JS | Optimized for minimal JS and server-heavy components |
PPR / DPR | Very high (edge shell + streaming) | High | High | High / Experimental | Medium | Personalized pages, hybrid pages | Experimental, combines SSR/ISR benefits, high complexity |
Conclusion:
Rendering patterns in Next.js aren’t one-size-fits-all solutions — they’re tools meant to be chosen based on what your project actually needs. For static content and strong SEO, methods like SSG or PPR usually work best. For more dynamic or real-time apps, CSR or SSR are better options. Approaches like ISR and DPR offer a nice middle ground, combining the speed of static pages with the flexibility of dynamic updates. And finally, RSC takes things a step further by pushing performance even more through server-side logic.
By learning how to use these different rendering patterns effectively, developers can build web apps that aren’t just fast and reliable, but also scalable and ready for whatever comes next.
Top comments (0)