A concise field‑guide to modern rendering patterns for React & Next.js.
Why this matters
Rendering strategy is the first user‑experience decision you make. It controls how soon real content appears, when it becomes
interactive, how heavy the JavaScript payload is, and whether crawlers can see it.
Before we compare patterns, let’s anchor on the key performance indicators (KPIs) that the book Learning Patterns recommends tracking:
| KPI | What it measures |
|---|---|
| TTFB | Time‑to‑first‑byte – server processing + round‑trip latency |
| FP / FCP | First (Contentful) Paint – when pixels arrive |
| LCP | Largest Contentful Paint – when the main hero appears |
| TTI | Time‑to‑interactive – JS executed & event handlers ready |
| TBT | Total Blocking Time – scripting & rendering main‑thread cost |
Large bundles push FCP/LCP forward; heavy server work pushes TTFB.
Your goal is to pick a pattern that balances these trade‑offs for YOUR page.
The spectrum at a glance
| Pattern | Where HTML is generated | Primary trade‑off | Next.js helper |
| ----------------------------------------------- | ----------------------- | ----------------- -------------------------------------- | -------------- ---------|
| CSR (Client‑Side Rendering) | Browser | Fast navigation after first load, but poor FCP/SEO for large bundles | Default |
| SSR (Server‑Side Rendering) | Server per request | Great FCP & SEO, but higher TTFB and hydration cost | getServerSideProps() |
| Static SSR (Incremental Static Gen.) | Build‑time & revalidated | Almost zero TTFB, yet fresh – good for marketing pages | revalidate option |
| SSR + Rehydration (Progressive / Streaming) | Server stream + client | Ships HTML quickly & hydrates progressively; complex | next/streaming |
| CSR + Prerender (Automatic Static Opt.) | Build‑time → CSR | Cached HTML for crawlers, SPA feel for users | Automatic |
| Full CSR | Browser only | Small apps, dashboards where SEO isn’t key | Default |
When to reach for which pattern
1. Client‑Side Rendering (CSR)
// index.jsx
import { useEffect, useState } from "react";
export default function Clock() {
const [time, setTime] = useState(() => new Date());
useEffect(() => {
const id = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(id);
}, []);
return <h1>{time.toLocaleTimeString()}</h1>;
}
Great for: real‑time dashboards, authenticated tools.
Watch out for: bundle bloat → slow FCP/FCP; SEO hurdles.
2. Server‑Side Rendering (SSR)
// pages/news.tsx
export async function getServerSideProps() {
const articles = await fetch("https://api.example.com/news").then(r => r.json());
return { props: { articles } };
}
export default function News({ articles }) {
return <ArticleList items={articles} />;
}
Great for: content sites where freshness & crawlability matter.
Watch out for: heavy back‑end queries → higher TTFB.
3. Static SSR / Incremental Static Generation (ISG)
export async function getStaticProps() {
const posts = await getAllPosts();
return { props: { posts }, revalidate: 60 }; // rebuild every minute
}
Great for: blogs, marketing pages – “fast by default” plus background re‑gen.
Watch out for: real‑time data – may need client fetch on top.
4. SSR with Rehydration & Streaming
Progressively streams HTML chunks (next/streaming), letting the browser paint while JS continues to hydrate.
Great for: long pages with hero above‑the‑fold, complex widgets further down.
Watch out for: brittle third‑party scripts, state management complexity.
5. CSR with Prerendering (Automatic Static Optimisation)
If a page has no blocking getServerSideProps call, Next.js prerenders it to static HTML and ships the JS bundle that will “take over” after hydration.
Great for: e‑commerce product catalog – first paint via HTML, cart logic via CSR.
Watch out for: large client JS – still download & parse after FCP.
Decision cheatsheet
| Your priority | Pick |
| ------------- ----------------|-------------------------------- |
| Instant first paint & SEO | SSR or Static SSR |
| Real‑time UI after login | CSR |
| Hybrid marketing + app | CSR with Prerender or Streaming |
| Global cacheability | Static SSR (ISG) |
Takeaways
- Rendering is a spectrum, not a binary choice.
- Measure KPIs first, then choose the simplest pattern that meets them.
- Next.js lets you mix patterns per page – exploit that flexibility.
✦ Excerpted & distilled from Learning Patterns by Lydia Hallie & Addy Osmani. 2025.
Top comments (0)