Choose optimal Next.js rendering strategy (SSR, SSG, ISR, CSR) based on content type, update frequency, and performance requirements. Use when deciding how to render pages, optimizing performance, or implementing data fetching. Trigger words include "rendering", "SSR", "SSG", "ISR", "static", "server-side".
Choose the optimal rendering strategy for Next.js pages based on requirements.
Decision criteria:
Ask these questions:
Static Site Generation (SSG):
// pages/products/[id].tsx
export async function getStaticProps({ params }) {
const product = await fetchProduct(params.id);
return {
props: { product },
// Optional: revalidate for ISR
// revalidate: 60, // seconds
};
}
export async function getStaticPaths() {
const products = await fetchAllProducts();
return {
paths: products.map(p => ({ params: { id: p.id } })),
fallback: 'blocking', // or false, or true
};
}
When to use SSG:
Server-Side Rendering (SSR):
// pages/dashboard.tsx
export async function getServerSideProps(context) {
const session = await getSession(context);
const data = await fetchUserData(session.userId);
return {
props: { data },
};
}
When to use SSR:
Incremental Static Regeneration (ISR):
// pages/blog/[slug].tsx
export async function getStaticProps({ params }) {
const post = await fetchPost(params.slug);
return {
props: { post },
revalidate: 60, // Regenerate every 60 seconds
};
}
When to use ISR:
Client-Side Rendering (CSR):
'use client'; // App Router
import { useEffect, useState } from 'react';
function Dashboard() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/user-data')
.then(res => res.json())
.then(setData);
}, []);
return <div>{data ? <Content data={data} /> : <Loading />}</div>;
}
When to use CSR:
App Router (Next.js 13+):
// app/products/page.tsx
async function ProductsPage() {
// SSG: cached by default
const products = await fetch('https://api.example.com/products');
// ISR: revalidate periodically
const products = await fetch('https://api.example.com/products', {
next: { revalidate: 3600 } // 1 hour
});
// SSR: no caching
const products = await fetch('https://api.example.com/products', {
cache: 'no-store'
});
return <ProductList products={products} />;
}
Pages Router:
// getStaticProps: SSG/ISR
// getServerSideProps: SSR
// useEffect + fetch: CSR
On-demand revalidation:
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
export async function POST(request) {
const path = request.nextUrl.searchParams.get('path');
if (path) {
revalidatePath(path);
return Response.json({ revalidated: true });
}
return Response.json({ revalidated: false });
}
Tagged caching:
// Fetch with tags
const data = await fetch('https://api.example.com/products', {
next: { tags: ['products'] }
});
// Revalidate by tag
revalidateTag('products');
getStaticPaths fallback options:
export async function getStaticPaths() {
return {
paths: [...],
fallback: false, // 404 for non-pre-rendered paths
// fallback: true, // Generate on-demand, show loading
// fallback: 'blocking', // Generate on-demand, wait for page
};
}
// Mix strategies in same app
// - SSG for marketing pages
// - ISR for blog posts
// - SSR for user dashboard
// - CSR for interactive features
// app/layout.tsx (SSG)
export default function RootLayout({ children }) {
return <html><body>{children}</body></html>;
}
// app/blog/[slug]/page.tsx (ISR)
async function BlogPost({ params }) {
const post = await fetch(`/api/posts/${params.slug}`, {
next: { revalidate: 60 }
});
return <Article post={post} />;
}
// app/dashboard/page.tsx (SSR)
async function Dashboard() {
const data = await fetch('/api/user', { cache: 'no-store' });
return <DashboardContent data={data} />;
}
// Show stale data immediately, revalidate in background
export async function getStaticProps() {
const data = await fetchData();
return {
props: { data },
revalidate: 1, // Revalidate every second
};
}
// Different rendering based on route
export async function getServerSideProps(context) {
const { preview } = context;
if (preview) {
// SSR for preview mode
const data = await fetchDraftContent();
return { props: { data, preview: true } };
}
// Redirect to SSG version
return {
redirect: {
destination: '/static-version',
permanent: false,
},
};
}
| Requirement | SSG | ISR | SSR | CSR | |------------|-----|-----|-----|-----| | SEO Critical | ✅ | ✅ | ✅ | ❌ | | Fast TTFB | ✅ | ✅ | ❌ | ❌ | | Fresh Data | ❌ | ⚠️ | ✅ | ✅ | | Personalized | ❌ | ❌ | ✅ | ✅ | | Large Scale | ⚠️ | ✅ | ✅ | ✅ | | Build Time | ❌ | ✅ | ✅ | ✅ |
✅ = Excellent, ⚠️ = Acceptable, ❌ = Poor
SSG:
ISR:
SSR:
CSR:
Build taking too long:
Stale data showing:
High server costs:
SEO issues:
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer