SSR vs SSG with Claude Code: Next.js and Astro Rendering Guide
Compare SSR, SSG, ISR, and static export in Next.js/Astro with Claude Code prompts, runnable examples, and checks.
Align the Terms First
The SSR vs SSG decision is not only a speed decision. SSR means server-side rendering: the server creates HTML when a request arrives. SSG means static site generation: HTML is produced during the build and served from a CDN or static host. ISR, or Incremental Static Regeneration, is the middle path: serve static output, then refresh it after a time window or an explicit revalidation event.
The beginner trap in modern Next.js is assuming that “server component” always means “SSR on every request.” It does not. In the current App Router docs for Dynamic Route Segments, TypeScript examples type params as a Promise. When you need to explicitly wait for a real request, the connection function is the current API.
When you ask Claude Code to “make this page faster,” it may overcorrect toward static output and accidentally freeze inventory, search results, or member-specific UI. When you ask for SSR everywhere, you can pay in TTFB and server cost. The practical move is to ask for a route matrix first: freshness, personalization, update frequency, monetization impact, and verification command for every route.
Comparison and Real Use Cases
| Question | SSG | ISR | SSR |
|---|---|---|---|
| When is HTML produced? | At build time | At build time and after revalidation | At request time |
| Best fit | Articles, docs, landing pages | Product pages, categories, news lists | Dashboards, search, carts |
| Data freshness | Build snapshot | Time or event based | Nearly current |
| Serving cost | Low | Medium | Often higher |
| Common failure | Long builds, stale content | A stale response can be served briefly | Slow responses without caching |
Use case 1 is a blog or documentation page. If the body, OGP metadata, internal links, and CTA are stable at publish time, SSG is the cleanest option. Rebuild when the article changes. If JavaScript size is also a concern, pair this with code splitting with Claude Code.
Use case 2 is an ecommerce product page. Prices, availability, and category order may change every few minutes, but most visitors should still get a fast static response. ISR fits that shape. The official Next.js ISR guide notes important constraints: ISR works with the Node.js runtime and is not supported by static export.
Use case 3 is an account dashboard. Cookies, billing status, permissions, unread counts, and private recommendations are request-specific. Treat this as SSR, and be explicit about cache headers. If your team is considering edge placement for dynamic routes, read edge computing with Claude Code after you decide which data can safely move closer to users.
Next.js SSR Example
This example is copy-pasteable in a Next.js App Router project and uses dummyjson.com, so no API key is required. It uses both connection() and cache: "no-store" to make the request-time behavior obvious during review.
// app/products/[id]/page.tsx
import { notFound } from "next/navigation";
import { connection } from "next/server";
type Product = {
id: number;
title: string;
price: number;
stock: number;
updatedAt: string;
};
async function getProduct(id: string): Promise<Product | null> {
await connection();
const res = await fetch(`https://dummyjson.com/products/${id}`, {
cache: "no-store",
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`Failed to load product: ${res.status}`);
const data = (await res.json()) as {
id: number;
title: string;
price: number;
stock: number;
meta?: { updatedAt?: string };
};
return {
id: data.id,
title: data.title,
price: data.price,
stock: data.stock,
updatedAt: data.meta?.updatedAt ?? new Date().toISOString(),
};
}
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await getProduct(id);
if (!product) notFound();
return (
<main>
<h1>{product.title}</h1>
<p>Price: ${product.price.toLocaleString("en-US")}</p>
<p>Stock: {product.stock}</p>
<p>Updated: {new Date(product.updatedAt).toLocaleString("en-US")}</p>
</main>
);
}
The main pitfall is adding cookies() or headers() to a shared layout because it feels convenient. Request-time APIs can make routes dynamic. If that shared layout wraps articles, static pages can silently move toward request-time rendering. Ask Claude Code to list every route affected by the change before accepting the patch.
SSG, ISR, and Static Export
SSG and ISR both feel static to the visitor, but they are not operated the same way. SSG is rebuilt when the site is built. ISR refreshes after a time window or an on-demand event. Static export uses output: "export" to create an out directory that can be served without a Node.js server. The official Next.js Static Exports guide explains that next build can generate one HTML file per route for static hosting.
// app/catalog/[id]/page.tsx
import { notFound } from "next/navigation";
export const revalidate = 3600;
type Product = {
id: number;
title: string;
description: string;
};
export async function generateStaticParams() {
return ["1", "2", "3"].map((id) => ({ id }));
}
async function getProduct(id: string): Promise<Product | null> {
const res = await fetch(`https://dummyjson.com/products/${id}`, {
next: { revalidate: 3600, tags: [`product:${id}`] },
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`Failed to load product: ${res.status}`);
return (await res.json()) as Product;
}
export default async function CatalogPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await getProduct(id);
if (!product) notFound();
return (
<article>
<h1>{product.title}</h1>
<p>{product.description}</p>
</article>
);
}
// next.config.mjs
const nextConfig = {
output: "export",
images: {
unoptimized: true,
},
};
export default nextConfig;
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"export:check": "next build && npx serve out"
}
}
Do not mix static export and ISR as if they were interchangeable. Static export is for static hosting. If the page needs cookies, on-demand revalidation, server functions, or real SSR, plan for a server runtime. Tell Claude Code whether the deployment target is Cloudflare Pages static hosting, a Node container, Vercel, Netlify, or another runtime before it writes code.
Astro Static and On-Demand Rendering
Astro starts from static generation by default. The official Astro on-demand rendering guide says pages, routes, and API endpoints are prerendered at build time by default, and individual routes can opt out with export const prerender = false after adding an adapter for the server runtime.
---
// src/pages/docs/[slug].astro
export async function getStaticPaths() {
const docs = [
{ slug: "ssr", title: "SSR guide" },
{ slug: "ssg", title: "SSG guide" },
];
return docs.map((doc) => ({
params: { slug: doc.slug },
props: { doc },
}));
}
const { doc } = Astro.props;
---
<html lang="en">
<body>
<article>
<h1>{doc.title}</h1>
<p>This page was generated at build time.</p>
</article>
</body>
</html>
---
// src/pages/account.astro
export const prerender = false;
const session = Astro.cookies.get("session")?.value;
const name = session ? "Masa" : "Guest";
Astro.response.headers.set("Cache-Control", "private, no-store");
---
<html lang="en">
<body>
<h1>Account</h1>
<p>Hello, {name}.</p>
</body>
</html>
The Astro failure mode is forgetting the adapter. A page may look correct locally, but on-demand rendering requires a server runtime in production. Ask Claude Code to inspect astro.config.mjs, the deployment platform, and every prerender override before changing content routes.
Verification Commands and Claude Code Prompt
Rendering strategy must be verified from build output, headers, cache logs, and performance metrics. For Next.js ISR, the official guide recommends testing production behavior with next build and next start. Do not stop at “the page renders in dev.”
# Next.js: production behavior
npm run build
npm run start
# In another terminal
curl -I http://localhost:3000/catalog/1
curl -I http://localhost:3000/products/1
# ISR cache debugging
NEXT_PRIVATE_DEBUG_CACHE=1 npm run start
# Astro
npm run build
npm run preview
Use a prompt that asks for the decision record, not just code:
Goal: classify every route in a Next.js/Astro site as SSR, SSG, ISR, or static export.
Inspect:
- app/ or src/pages/ routes
- cookies(), headers(), searchParams, connection(), cache: "no-store"
- generateStaticParams(), revalidate, output: "export", Astro prerender
- monetization pages, member pages, articles, product pages, and search pages
Constraints:
- Do not break SEO metadata or internal links
- Keep npm run build passing before and after
- Return a table with the chosen strategy, reason, touched files, and verification command
Specific Pitfalls
The first pitfall is putting user-specific content into a static article body. Personalized recommendations, member names, and private prices should not be baked into SSG HTML. Keep the article static and move personalization to a client component or a deliberately dynamic section.
The second pitfall is setting ISR intervals too low. revalidate = 1 often moves the cost back to the server without a clear benefit. If precision matters, use on-demand revalidation. If the data must be real time, use SSR.
The third pitfall is adding server-only features after choosing static export. Auth, cookie-based UI, server route handlers, and ISR need a runtime. Static hosting is excellent, but it is not a free replacement for a server.
The fourth pitfall is accepting Claude Code’s fastest-looking proposal without checking the business surface. For a monetized article site, performance is only one part of quality. OGP, structured data, internal links, ad placement, CTA tracking, LCP, and CLS all matter. After the rendering decision, use the checklist in performance optimization with Claude Code.
Monetization-Aware Recommendation
For a ClaudeCodeLab-style content business, default to SSG for articles, tutorials, comparisons, and top-of-funnel pages. Use ISR for product catalogs, category pages, and news-like surfaces that can tolerate short-lived stale content. Use SSR for member pages, search, carts, checkout context, and anything that depends on private request data.
To start with a practical workflow, get the free Claude Code cheatsheet. For reusable templates and implementation packs, use the products page. For teams that need route strategy, permissions, code review, analytics, and rollout training, start from Claude Code training and consultation.
What I Actually Verified
For this refresh, I checked the official Next.js Dynamic Route Segments, connection, ISR, Static Exports, Astro on-demand rendering, and Claude Code overview pages. In Masa’s content workflow, the most stable split was SSG for articles, ISR for product and category listings, and SSR for account and search surfaces. The highest-leverage Claude Code step was asking for the route classification table before code changes. It exposed accidental cookie usage in static pages early, and it made the final review much faster because every route had a reason and a verification command.
Free PDF: Claude Code Cheatsheet
Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.
We handle your data with care and never send spam.
Level up your Claude Code workflow
If you are comparing tools, do not stop at the verdict. Grab the free cheatsheet for daily command fluency, use the prompt pack to raise output quality, and use the setup guide if you plan to adopt Claude Code seriously.
About the Author
Masa
Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.
Related Products
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.