Static or Dynamic? Why not both?
For a decade, we've had to choose. Static Site Generation (SSG) gave us speed but stale data. Server Side Rendering (SSR) gave us fresh data but slow TTFB.
Partial Pre-rendering (PPR) breaks this binary. It is the quantum superposition of web architecture.
The Dilemma: You're building an E-commerce product page. The Navbar, Footer, and Product Description are static—they never change. The "Add to Cart" button and "Related Products" are dynamic—they depend on user cookies and inventory.
Traditionally, if *one* part is dynamic, the *whole* page has to be SSR. You lose that instant "Edge" delivery. PPR allows the static shell to be served instantly from the edge (0ms LCP), while the dynamic holes are streamed in parallel.
02. What is Partial Pre-rendering?
PPR is a compiler optimization that treats your React tree as a static shell with dynamic "holes".
It uses React Suspense boundaries to delimit these zones. Anything inside a Suspense boundary that reads dynamic data (cookies, headers, non-cached fetch) is considered dynamic. Everything else is pre-rendered at build time.
// app/product/[id]/page.js
// 🟢 STATIC SHELL (Pre-rendered)
export default function Page({ params }) {
return (
<main>
<Header />
<ProductDetails id={params.id} />
{/* 🔴 DYNAMIC HOLE (Streamed) */}
<Suspense fallback={<CartSkeleton />}>
<UserCart />
</Suspense>
{/* 🔴 DYNAMIC HOLE (Streamed) */}
<Suspense fallback={<ReviewsSkeleton />}>
<PersonalizedReviews />
</Suspense>
<Footer />
</main>
);
}
03. The Request-Response Lifecycle
1. The Immediate Response
The Edge CDN instantly returns the pre-computed HTML shell. The user sees the header, footer, and loading skeletons immediately. TTFB is effectively ~10-20ms.
2. The Dynamic Stream
The server (or lambda) starts executing the dynamic parts. As they complete, chunks of HTML/Script are streamed into the existing response, replacing the skeletons.