Rendering Strategies Explained: SSR vs CSR vs SSG vs ISR
The single most common system design question for senior frontend roles is: "What rendering strategy would you use for X, and why?"
Most developers can name the four strategies. Fewer can explain the trade-offs with precision. This guide gives you both.
Why It Matters for Your Interview
Rendering strategy affects three things interviewers care about: 1. SEO — can search engines index the content? 2. Performance — what are TTFB and LCP? 3. Data freshness — how stale can the content be?
Your answer should address all three for each strategy.
---
CSR — Client-Side Rendering
The browser downloads a nearly empty HTML file and a JavaScript bundle. React runs in the browser and renders the page.
What Googlebot sees immediately:
<html> <body> <div id="root"></div> <!-- empty until JS executes --> <script src="/bundle.js"></script> </body> </html>
When it's the right choice:
- Authenticated dashboards (user-specific content, no SEO needed)
- Admin panels and internal tools
- Highly interactive apps where pre-rendering provides no value
When it fails:
- Any page that needs Google/Bing indexing
- Pages where users on slow connections see a blank screen for 3+ seconds
Key trade-off: Excellent developer experience, poor initial load experience for real users on slow connections.
---
SSR — Server-Side Rendering
The server runs the React component tree on every request and sends complete HTML to the browser. Users see content immediately. The browser then "hydrates" — attaches React's event handlers to the server-rendered HTML.
// Next.js Pages Router
export async function getServerSideProps(context) {
const product = await getProduct(context.params.id);
return { props: { product } };
}
// Runs on every single request — HTML is fully populated
When it's the right choice:
- Pages with request-time data (user-specific personalization + SEO needed)
- News pages where real-time freshness matters
- Pages that need cookies/headers at render time
When it's wrong:
- Mostly-static content — you're paying server costs unnecessarily
- High-traffic pages — every request hits your server
Key trade-off: High TTFB because the server must fetch data before responding. Doesn't cache cleanly on CDN.
---
SSG — Static Site Generation
React runs at build time. The output is static HTML files served from a CDN. No server needed at runtime.
// Runs once at build time — not per request
export async function getStaticProps() {
const posts = await getAllPosts();
return { props: { posts } };
}
export async function getStaticPaths() { const slugs = await getAllSlugs(); return { paths: slugs.map(slug => ({ params: { slug } })), fallback: false, }; }
When it's the right choice:
- Marketing pages, landing pages, documentation
- Blog posts, technical articles
- Any content that doesn't change between deployments
When it's wrong:
- Content that changes frequently (product prices, stock)
- Millions of pages — build time becomes impractical
Key trade-off: Fastest possible delivery from CDN. Content is stale between builds.
---
ISR — Incremental Static Regeneration
ISR is SSG with a background refresh. Pages are statically generated at build time, but a revalidate interval tells Next.js to regenerate the page in the background after a set time.
export async function getStaticProps() {
const product = await getProduct();
return {
props: { product },
revalidate: 3600, // regenerate every hour at most
};
}
The stale-while-revalidate behaviour: 1. First visitor after build: served static HTML ✅ 2. Visitor after 1 hour: still served old HTML (instantly), but a background regen is triggered 3. Next visitor: sees the freshly generated page
When it's the right choice:
- E-commerce product pages (inventory/price changes hourly)
- News category pages (new articles, not breaking news)
- Marketing pages with A/B test variants
Key trade-off: One visitor after the revalidate window sees stale data while fresh is being built. For most use cases, this is acceptable.
---
Streaming SSR (React 18 + Next.js App Router)
Instead of waiting for all server data before sending HTML, streaming sends HTML in chunks. Fast parts (navbar, hero) arrive immediately. Slow parts (comments, recommendations) stream in later using Suspense boundaries.
export default function ProductPage() {
return (
<div>
<ProductHero /> {/ fast — no data needed /}
<Suspense fallback={<Spinner />}>
<ProductRecommendations /> {/ slow — waits for API /}
</Suspense>
<Suspense fallback={<ReviewSkeleton />}>
<ProductReviews /> {/ slow — streams separately /}
</Suspense>
</div>
);
}
Result: User sees the hero immediately. Reviews stream in as the server fetches them. TTFB is low; total page load is still full.
---
The Decision Framework
| Strategy | SEO | TTFB | Data Freshness | Server Cost | |----------|-----|------|----------------|-------------| | CSR | ❌ | Fast | Real-time | None | | SSR | ✅ | Slow | Per-request | High | | SSG | ✅ | Instant | Build-time | None (CDN) | | ISR | ✅ | Instant | Periodic | Minimal | | Streaming | ✅ | Fast | Per-request | Moderate |
For interviews, answer with three questions: 1. Does this page need SEO? (No → CSR is fine) 2. How fresh does the data need to be? (Hourly → ISR, per-request → SSR, build-time → SSG) 3. Is the content personalized? (Yes → SSR or CSR)
---
Common Interview Questions
Q: "How would you render a product listing page on an e-commerce site?" ISR with revalidate: 3600. Products don't change by the second. CDN serves HTML instantly. Background refresh every hour keeps prices and inventory current without server cost per request.
Q: "What's the difference between SSG and ISR?" SSG generates pages once at build time — content is fixed until the next deployment. ISR generates pages at build time but regenerates them in the background after a configurable interval, without requiring a full redeploy.
Q: "Why is Next.js App Router different?" App Router uses React Server Components by default — components can be server-only (no JS shipped to client) and the mental model shifts from "getServerSideProps" to "async component functions that fetch data directly". Streaming is built-in via Suspense.