
Next.js Rendering
ā² Next.js Rendering Strategies
Next.js supports multiple rendering strategies. Static is fastest. Dynamic is freshest. ISR gives you both.
Here's how to pick the right strategy:
ā Static rendering (default) ā Dynamic rendering ā Incremental Static Regeneration (ISR) ā Streaming
#nextjs #react #ssr #ssg #streaming #webdev #frontend #coding #tips
Next.js renders pages in different ways depending on the data and APIs you use. Choosing the right strategy affects performance and freshness.
| Strategy | When it runs | Best for |
|---|---|---|
| Static (SSG) | Build time | Content that rarely changes |
| Dynamic (SSR) | Every request | Personalized or real-time data |
| ISR | Build + revalidate | Content that changes occasionally |
| Streaming | Progressively | Slow data with fast shell |
Static rendering (default)
Pages are rendered at build time and served from a CDN.
// Rendered at build time by default
export default async function Blog() {
const posts = await getPosts();
return <PostList posts={posts} />;
}A page is static when it uses no dynamic APIs or uncached data.
Dynamic rendering
Pages render on every request. Triggered by dynamic APIs or uncached fetches.
Dynamic APIs force dynamic rendering:
import { cookies, headers } from "next/headers";
export default async function Profile() {
const cookieStore = await cookies();
const theme = cookieStore.get("theme");
}Uncached fetch also forces dynamic rendering:
const data = await fetch("https://api.example.com", { cache: "no-store" });Force dynamic rendering with a route segment config:
export const dynamic = "force-dynamic";Incremental Static Regeneration (ISR)
Update static content without rebuilding the entire site. Pre-render pages at build time, then revalidate them in the background.
app/blog/[id]/page.tsx
export const revalidate = 60; // revalidate at most every 60 seconds
export async function generateStaticParams() {
const posts = await fetch("https://api.example.com/posts").then((res) =>
res.json(),
);
return posts.map((post) => ({ id: String(post.id) }));
}
export default async function Page({ params }) {
const { id } = await params;
const post = await fetch(`https://api.example.com/posts/${id}`).then((res) =>
res.json(),
);
return <Post post={post} />;
}How it works:
generateStaticParamsreturns the pages to pre-render at build time.- Requests to those pages are served instantly from cache.
- After
revalidateseconds, the next request still gets the stale page, and Next.js regenerates it in the background. - Once regenerated, subsequent requests get the fresh version.
- Paths not in
generateStaticParamsare generated on demand at first request (unlessdynamicParams = false).
On-demand revalidation
Invalidate the cache from a server action or route handler instead of waiting for the interval.
"use server";
import { revalidatePath, revalidateTag } from "next/cache";
export async function createPost() {
revalidatePath("/posts"); // invalidate an entire route
revalidateTag("posts", "max"); // or invalidate tagged fetches
}Tag individual fetches for granular control.
fetch("https://api.example.com/posts", { next: { tags: ["posts"] } });Regeneration happens on the next request after invalidation, not immediately.
Streaming
Render the page shell instantly and stream in slow parts with Suspense.
import { Suspense } from "react";
export default function Dashboard() {
return (
<div>
<Header /> {/* renders immediately */}
<Suspense fallback={<Skeleton />}>
<SlowChart /> {/* streams in when ready */}
</Suspense>
</div>
);
}loading.tsx wraps the page in Suspense automatically.
Full-Stack AI Developer Roadmap
From HTML & CSS to working with AI models, all in one structured roadmap.