Rendering Modes: Static, Dynamic, and ISR
Next.js 15 supports multiple rendering strategies. Choosing the right one for each route determines both performance and data freshness.
Fully Static Routes
When a page has no dynamic data, Next.js renders it at build time:
// app/about/page.tsx — no async data = fully static
export default function AboutPage() {
return <h1>About Us</h1>;
}
generateStaticParams for Dynamic Static Routes
Pre-render all known slugs at build time:
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await db.post.findMany({
select: { slug: true },
where: { published: true },
});
return posts.map(post => ({ slug: post.slug }));
}
export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await db.post.findUnique({ where: { slug } });
if (!post) notFound();
return <article>{post.content}</article>;
}
Incremental Static Regeneration (ISR)
Re-generate static pages in the background after they are visited:
// app/blog/[slug]/page.tsx
// Revalidate this page every 60 seconds after first request
export const revalidate = 60;
// On-demand: regenerate when new posts are published
// Call revalidatePath('/blog/new-post-slug') from a Server Action
The Rendering Decision Tree
Is there dynamic data (cookies, headers, searchParams)?
→ YES: Dynamic rendering (per request)
→ NO: Does the route use revalidate?
→ YES: ISR (stale-while-revalidate)
→ NO: Fully static (at build time)
Combining Strategies with Partial Prerendering
// app/product/[id]/page.tsx
export const experimental_ppr = true;
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await getProduct(id); // Static shell
return (
<div>
<ProductDetails product={product} /> {/* Static */}
<Suspense fallback={<ReviewsSkeleton />}>
<ProductReviews productId={id} /> {/* Dynamic stream */}
</Suspense>
<Suspense fallback={<RecommendationsSkeleton />}>
<Recommendations productId={id} /> {/* Dynamic stream */}
</Suspense>
</div>
);
}
PPR delivers the static product details instantly while the personalized sections stream in — best of both worlds.