Rendering Pattern
What It Is
Rendering pattern means deciding where and when a page is generated and where data is fetched.
In Next.js, CSR, SSR, and SSG solve the same problem, but they differ in timing, execution place, initial HTML, SEO, and performance.
Rendering Pattern = When + Where data is fetched and shown on the UI
Rendering Categories
Rendering patterns are mainly split between client and server.
Client -> CSR
Server -> SSR / SSG
App Router -> React Server Components + Client Components
In simple terms:
- CSR renders in the browser.
- SSR renders on the server for every request.
- SSG renders at build time.
- RSC runs components on the server by default.
- Client Components are used when interactivity is needed.
Client-Side Rendering
CSR stands for Client-Side Rendering.
In CSR, the page first loads in the browser, then JavaScript fetches data and renders the UI.
Users may initially see a loading state before the real content appears.
CSR = Browser loads page shell -> JS fetches data -> Browser renders UI
CSR Example
import { useEffect, useState } from "react";
export default function CsrPage() {
const [data, setData] = useState(null);
useEffect(() => {
fetch("http://localhost:3000/courses")
.then((res) => res.json())
.then(setData);
}, []);
if (!data) return <p>Loading...</p>;
return (
<div>
{data.map((course) => (
<h2 key={course.id}>{course.title}</h2>
))}
</div>
);
}
CSR Use Case
CSR is best suited for highly interactive UI.
Good examples:
- dashboards
- filters
- search pages
- UI where data depends on user actions
CSR downside:
- slower initial content load
- weaker SEO because content is not present in the initial HTML
Server-Side Rendering
SSR stands for Server-Side Rendering.
In SSR, data is fetched on the server for every request.
The server sends fully rendered HTML to the browser, so users see complete content earlier.
SSR = Request -> Server fetches data -> Server renders HTML -> Browser receives content
SSR Example
export async function getServerSideProps() {
const res = await fetch("http://localhost:3000/courses");
const data = await res.json();
return {
props: { data },
};
}
export default function SsrPage({ data }) {
return (
<div>
{data.map((course) => (
<h2 key={course.id}>{course.title}</h2>
))}
</div>
);
}
SSR Use Case
SSR is useful when:
- data changes frequently
- SEO is important
- page content should be fresh per request
SSR tradeoff:
- higher server load
- slower response than static pages because rendering happens per request
Static Site Generation
SSG stands for Static Site Generation.
In SSG, data is fetched at build time and static HTML files are generated.
Those static files are then served quickly to users.
SSG = Build time data fetch -> Static HTML generated -> Browser receives static page
SSG Example
export async function getStaticProps() {
const res = await fetch("http://localhost:3000/courses");
const data = await res.json();
return {
props: { data },
};
}
export default function SsgPage({ data }) {
return (
<div>
{data.map((course) => (
<h2 key={course.id}>{course.title}</h2>
))}
</div>
);
}
SSG Use Case
SSG is ideal for content that does not change often.
Good examples:
- blogs
- marketing pages
- public content
- documentation pages
SSG is usually the fastest option because the page is already generated.
Incremental Static Regeneration
ISR stands for Incremental Static Regeneration.
ISR allows a static page to update in the background after a specific time.
revalidate: 60;
This is useful when you want static page speed but still need occasional content updates.
SSG = static until rebuild
ISR = static page + background updates
CSR vs SSR vs SSG
| Feature | CSR | SSR | SSG |
|---|---|---|---|
| Execution | Browser | Server per request | Build time |
| Data Fetching | After initial render | Before response is sent | During build |
| Initial HTML | Minimal shell | Fully populated HTML | Static pre-rendered HTML |
| TTFB | Fast shell | Slower due to server processing | Very fast |
| LCP | Slower because content depends on JS | Good because HTML contains content | Best because content is pre-generated |
| Hydration | Full app hydration | HTML + hydration | Static HTML + hydration |
| SEO | Weak | Strong | Strong |
| Caching | Browser cache / client state | Needs explicit caching | Naturally cacheable with CDN |
| Scalability | High client-side load | Limited by server capacity | Extremely high |
| Freshness | Real-time client fetch | Fresh per request | Stale unless rebuilt or ISR is used |
| Best Fit | Interactive apps and dashboards | Dynamic content and personalization | Blogs, marketing pages, public content |
| Worst Fit | SEO-heavy pages | High-traffic static pages | Frequently changing real-time data |
React Server Components
In Next.js App Router, components run on the server by default.
This means no JavaScript is sent to the browser unless it is needed.
RSC improves performance by moving data fetching and rendering to the server.
Server Component = Data fetching + rendering on server
Server Component Example
export default async function Page() {
const res = await fetch("http://localhost:3000/courses");
const courses = await res.json();
return (
<div>
{courses.map((course) => (
<h2 key={course.id}>{course.title}</h2>
))}
</div>
);
}
Server Component behavior:
- data is fetched on the server
- no client JavaScript is sent for this part
- initial render is fast
Client Components
Client Components are used when browser interactivity is needed.
To create a Client Component, use:
"use client";
Client Components can handle:
- state
- events
- input changes
- user interactions
Client Component Example
"use client";
import { useState } from "react";
export default function Filter() {
const [query, setQuery] = useState("");
return (
<input
placeholder="Search courses..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
);
}
Client Component tradeoff:
- enables interactivity
- increases bundle size
- requires hydration
Combined Server and Client Usage
A common pattern is to fetch and render data on the server, then use Client Components only for interactive parts.
import Filter from "./Filter";
export default async function Page() {
const res = await fetch("http://localhost:3000/courses");
const courses = await res.json();
return (
<div>
<Filter />
{courses.map((course) => (
<h2 key={course.id}>{course.title}</h2>
))}
</div>
);
}
Server -> data + rendering
Client -> interaction
This keeps the page fast while still allowing interactive UI.
How to Choose
| Need | Best Pattern |
|---|---|
| Highly interactive UI | CSR |
| Fresh data on every request | SSR |
| SEO with dynamic data | SSR |
| Static public content | SSG |
| Static speed with periodic updates | ISR |
| Less JavaScript and server-side data fetching | RSC |
| State, events, and browser interaction | Client Component |
Basic Checklist
Use CSR for interactive dashboards and filters
Use SSR when data changes often or SEO matters
Use SSG for blogs, marketing pages, and public content
Use ISR when static pages need background updates
Use Server Components for server-side data and less client JS
Use Client Components only when interactivity is needed
Avoid sending unnecessary JavaScript to the browser
Choose rendering based on freshness, SEO, interactivity, and scalability
Interview Style Answer
Rendering patterns define where and when a page is generated and where data is fetched. CSR renders in the browser after JavaScript loads, so it is useful for highly interactive pages but weaker for SEO and initial load. SSR fetches data and renders HTML on the server for every request, giving fresh content and better SEO but increasing server load. SSG fetches data at build time and serves static HTML, making it very fast and scalable for blogs, marketing pages, and public content. ISR extends SSG by allowing static pages to update in the background. In Next.js App Router, React Server Components run on the server by default to reduce client JavaScript, while Client Components with "use client" are used for interactivity like state and events.
One-Line Summary
Rendering Pattern = Choose CSR, SSR, SSG, ISR, RSC, or Client Components based on SEO, freshness, interactivity, and performance.
Final Mental Model
CSR -> Browser renders
SSR -> Server renders per request
SSG -> Build generates static HTML
ISR -> Static HTML updates in background
RSC -> Server component by default
Client Component -> Browser interactivity