Skip to main content

SSR vs SSG vs ISR in Next.js

Answer

Next.js supports multiple rendering strategies: Server-Side Rendering (SSR), Static Site Generation (SSG), and Incremental Static Regeneration (ISR).

Rendering Timeline

Comparison

AspectSSGSSRISR
When generatedBuild timeEach requestBuild + background
PerformanceFastestSlowerFast
Data freshnessStaleAlways freshConfigurable
Use caseBlogs, docsUser dashboardsE-commerce
CDN cacheableYesNoYes

SSG - Static Site Generation

// PAGES ROUTER
export async function getStaticProps() {
const posts = await fetchPosts();
return {
props: { posts },
};
}

export default function Blog({ posts }) {
return posts.map((post) => <Post key={post.id} post={post} />);
}

// APP ROUTER (default behavior)
async function BlogPage() {
const posts = await fetch("/api/posts", { cache: "force-cache" });
return posts.map((post) => <Post key={post.id} post={post} />);
}

SSR - Server-Side Rendering

// PAGES ROUTER
export async function getServerSideProps(context) {
const user = await getUser(context.req.cookies.token);
return {
props: { user },
};
}

export default function Dashboard({ user }) {
return <div>Welcome, {user.name}</div>;
}

// APP ROUTER
async function DashboardPage() {
const user = await fetch("/api/user", {
cache: "no-store", // SSR - always fresh
});
return <div>Welcome, {user.name}</div>;
}

ISR - Incremental Static Regeneration

// PAGES ROUTER
export async function getStaticProps() {
const products = await fetchProducts();
return {
props: { products },
revalidate: 60, // Regenerate every 60 seconds
};
}

// APP ROUTER
async function ProductsPage() {
const products = await fetch("/api/products", {
next: { revalidate: 60 }, // Revalidate every 60 seconds
});
return <ProductList products={products} />;
}

On-Demand Revalidation

// API route to trigger revalidation
// pages/api/revalidate.js (Pages Router)
export default async function handler(req, res) {
await res.revalidate("/products");
return res.json({ revalidated: true });
}

// app/api/revalidate/route.js (App Router)
import { revalidatePath, revalidateTag } from "next/cache";

export async function POST(request) {
revalidatePath("/products");
// or revalidateTag('products');
return Response.json({ revalidated: true });
}

Decision Flowchart

Best Practices

// Mix strategies in one app
// app/page.js - SSG (home page)
// app/blog/page.js - ISR (content updates periodically)
// app/dashboard/page.js - SSR (user-specific)

// Dynamic segments with SSG
// pages/blog/[slug].js
export async function getStaticPaths() {
const posts = await getPosts();
return {
paths: posts.map((post) => ({ params: { slug: post.slug } })),
fallback: "blocking", // or true, or false
};
}

export async function getStaticProps({ params }) {
const post = await getPost(params.slug);
return { props: { post }, revalidate: 3600 };
}

Use Cases

StrategyBest For
SSGMarketing pages, blogs, documentation
SSRUser dashboards, personalized content, real-time data
ISRProduct pages, news sites, frequently updated content

Key Points

  • SSG: Pre-built at build time, cached on CDN
  • SSR: Fresh content on every request
  • ISR: Static with background regeneration
  • App Router uses fetch cache options
  • Pages Router uses getStaticProps/getServerSideProps
  • Use on-demand revalidation for immediate updates
  • Choose based on data freshness requirements