Hook — 05 : 28 a.m., Lisbon ↔ Lagos bug hunt
Tariq’s WhatsApp message hit before sunrise: “Our product docs take 45 seconds to update after a new release.” He was on hotel Wi-Fi in Lagos; I was three hours ahead, cradling espresso on a tiled balcony. Their Next.js site used classic Static Site Generation—great for speed, terrible for freshness. We pair-programmed across time zones, flipped two pages to Incremental Static Regeneration, redeployed to Vercel, and voilà: updated content shipped in <60 seconds while keeping the same lightning-fast First Contentful Paint. This cross-continental sprint is our compass today: we’ll demystify SSG versus ISR, show where React 18’s streaming server renderer fits in, and give you copy-paste snippets to modernize any docs or blog-style project.
Why This Topic Still Matters in 2025
Challenge | Real-World Impact | How SSG / ISR Helps |
---|---|---|
Core Web Vitals penalties for slow Time to First Byte | Lower SEO, fewer eyeballs | Pre-render HTML at build or edge |
Growing content velocity (daily docs, weekly releases) | Rebuilds take 15 min+; PRs blocked | ISR re-generates pages on-demand |
Global readers on flaky 3G | JS-only apps show blank screens | SSG streams usable HTML instantly |
Rising maintenance cost | Multiple caching layers | Next.js unifies with revalidate flag |
React 18 turbo-charges both strategies with streaming—renderToPipeableStream
sends HTML chunks as soon as data resolves, shaving hundreds of milliseconds even on 4G.
Concept Check — SSG vs. ISR in Plain English
Static Site Generation (SSG)
textCopyEditnpm run build → next build
Next.js renders every page to a flat HTML file at build time. When users hit /docs/getting-started
, the CDN returns cached HTML—no server logic. Perfect for stable content (marketing pages, blog posts).
Incremental Static Regeneration (ISR)
tsCopyEditexport const revalidate = 60 // seconds
Next.js still pre-renders, but requests older than revalidate
seconds trigger a background rebuild of that page only. Users always get a static response; you get nearly real-time content freshness.
Rule of Thumb
If your data rarely changes, ship SSG. If it changes more than once a deployment, layer ISR.
Project Bootstrap in Two Commands
bashCopyEditnpx create-next-app@latest docs-demo --typescript --eslint
cd docs-demo && npm run dev
Out of the box, the React 18–powered pages/
router SSGs every file in pages/
, while the app/
router streams server components. We’ll focus on app/
for modern ISR.
Walkthrough 1 — Classic SSG Page
Create app/guide/page.tsx
:
tsxCopyEditimport { Markdown } from '@/components/Markdown'; // hypothetical MD renderer
// Build-time fetch
async function getGuide() {
const res = await fetch(
'https://cms.mycorp.com/api/guide',
{ next: { revalidate: 0 } } // 0 = static forever
);
return res.json();
}
export default async function GuidePage() {
const guide = await getGuide();
return (
<main className="prose mx-auto">
<h1>{guide.title}</h1>
<Markdown>{guide.body}</Markdown>
</main>
);
}
Why it’s fast
- HTML rendered during
next build
- Served from CDN edge; no origin hit
- Hydrates on the client via React 18 concurrently, but FCP is pure HTML
Walkthrough 2 — Switching to ISR for Freshness
Product docs update hourly; rebuilds can’t keep up. Swap revalidate
:
tsxCopyEditasync function getGuide() {
const res = await fetch(
'https://cms.mycorp.com/api/guide',
{ next: { revalidate: 300 } } // 5 minutes
);
return res.json();
}
What happens under the hood
- First request after 5 min serves stale HTML immediately.
- Next.js triggers background re-generation using React 18 streaming.
- When done, CDN swaps the HTML atomically—zero downtime.
- Subsequent visitors see fresh content.
Edge case: Real-time dashboards need <10 s freshness. Use client-side fetching or server components with cache-control headers instead.
Remote-Work Insight 🌐
Sidebar (≈140 words)
Our docs team spans Manila, Madrid, and Montréal. A midnight release used to require a full production rebuild—30 minutes, blocking QA across regions. We migrated to ISR withrevalidate: 60
, added a Slack bot that pings “Page re-generated ✅” via Vercel webhooks, and merged time-zone workflows: Asia authors push edits; by the time Europe logs in, pages are updated without manual deploys. DevOps sleeps better; editors hit publish without Slack summons.
Code Snippet — Conditional ISR in pages/
router (fallback)
tsxCopyEdit// pages/blog/[slug].tsx
export const getStaticPaths = async () => {
const posts = await fetchCMSList();
return {
paths: posts.map(p => ({ params: { slug: p.slug } })),
fallback: 'blocking', // SSG + ISR hybrid
};
};
export const getStaticProps = async ({ params }) => {
const post = await fetchPost(params!.slug);
return {
props: { post },
revalidate: 600, // rebuild each 10 min
};
};
Users hitting a new slug get the page generated on demand (“blocking”); subsequent hits serve static HTML.
Common Pitfalls & Fixes
Bug | Symptom | Fix |
---|---|---|
Stale env vars | ISR uses old API URL | Set env: in Vercel project, redeploy to pick up |
Long regeneration times | First visitor waits | Use fallback: 'blocking' or pre-warm via webhook |
Cache bust loops | fn returns random Date.now() | Remove non-deterministic props; memoize |
Performance & Accessibility Checkpoints
- TTFB: Aim <200 ms at global edges; ISR pages may spike during regeneration—monitor Apdex.
- CLS: Pre-define image dimensions via
next/image
; server-rendered HTML prevents late shifts. - Color Contrast & Focus: Even static HTML must satisfy WCAG; run Axe on generated pages.
- React DevTools Profiler: Hydration should show one commit; streaming in React 18 reduces blocking.
Helpful Command & Tool Table
CLI / Tool | Purpose |
---|---|
npm run build | Generates SSG & ISR manifests |
vercel deploy | Uploads static + serverless functions |
next build --profile | Outputs trace of slow pages |
@next/bundle-analyzer | Identify heavy client chunks |
Diagram Idea (describe for SVG)
Timeline of Requests:
- t0: Visitor → CDN → HTML cached ✔️
- t+5 min: New visitor → CDN (stale) → triggers ISR → Background regeneration (purple bar)
- t+5 min+gen: CDN swaps to new HTML
Labels: SSG, ISR trigger, React 18 streaming, Hydration.
Wrap-Up
Static Site Generation gives you instant paint; Incremental Static Regeneration adds near-real-time updates without wrecking performance. Thanks to React 18 streaming, ISR pages now show meaningful HTML even while data fetches late. Start with SSG, sprinkle ISR with revalidate
, and your docs, blog, or e-commerce catalog stays light and current—whether your editor sits in Nairobi or New York. Questions? War stories? Drop them below; I’ll answer somewhere between layovers and asynchronous code reviews.