Partial Prerendering (PPR)
PPR is the ultimate rendering optimization. It pre-renders the static shell of your page at build time and streams dynamic content into 'holes' as soon as it's ready.
In this experimental build, PPR defaults to Dynamic Streaming. Refresh to see the server-side time update.
Strategy
Experimental Stage: Currently available in Next.js 14/15 via an experimental flag. It enables 'incremental' rollout, allowing you to opt-in per route.
Hydration
Server sends finished HTML. Browser 'hydrates' it for interactivity.
Inspect Network
Open DevTools > Network. Look forCache-Controlheader. For PPR, you will seeno-store / no-cache
The "Why": PPR vs Standard Dynamic Rendering
You might ask: "If I use Layouts and Suspense, why do I need PPR?"The answer isn't about what is rendered, but where it comes from.
Experimental Note: PPR is currently defaulting to SSR in your Local Build
Standard SSR / Dynamic Rendering
User Requests Page
Server receives request.
Dynamic Check
Detects `cookies()` or `headers()`.
Blocking Wait
Server waits for ALL data before sending HTML.
TTFB: Slow
Browser sees nothing until server is done.
Partial Prerendering (Goal)
User Requests Page
CDN receives request.
Instant Shell
CDN sends static Layout (Nav/Sidebar) instantly.
Streaming Holes
Dynamic data streams into Suspense holes.
TTFB: Instant
Browser renders shell in 0ms.
The CDN Caching Difference
Without PPR
A dynamic route (using cookies) cannot be cached as a static file. The browser must wait for the server to run the layout code for every single visit.
With PPR
The Layout (Sidebar/Nav) is pre-rendered to a static HTML file at build time. The CDN serves this file instantly, while only the specific dynamic holes are streamed from the server.
The Result
You get the Time-to-First-Byte of a static site with theflexibility of a dynamic app. This is why it's the future of Next.js performance.
The PPR Request Pipeline
Step-by-step visual of "Static Shell + Dynamic Streaming"
Note: Fallback to SSR might occur in experimental builds
User Requests Page
User hits the URL. No pre-processing needed for the static shell.
Edge Sends Static Shell
Instantly delivers Navigation, Layout, and Skeletons from the CDN. (0ms Wait)
Server Resolves 'Hole'
Next.js runs the async database queries only for the dynamic Suspense blocks.
Stream Dynamic Segment
Data is ready! The server streams the final component HTML over the same connection.
Instant Hydration
React swaps the skeleton for the real content. Page is now fully interactive.
When to use PPR?
Landing Pages
Instant SEO Shell + Promo data
User Settings
Highly private, no static benefit
Product Detail
Static specs + Live stock status
Admin Dash
Better as client-side dynamic
Astro Note: Unlike Astro which uses "Islands" to control client JS, PPR uses "Holes" to control server streaming. You get the same result (fast loads) but via a unified server-first pipeline.
The Technical Blueprint
Next.js automatically maps your React code into the PPR pipeline. The secret lies in combining Suspense with Dynamic APIs like `cookies()`, `headers()`, or uncached data fetches.
How to implement in Code?
// 🟢 THE STATIC SHELL// Rendered at build-time, sent instantly via CDN.export default function Page() { return ( <main> <Navbar /> <HeroSection /> {/* Static Content */} <Suspense fallback={<Skeleton />}> <DynamicUserDash /> </Suspense> </main> );}You MUST use React Suspense. This is the boundary that signals to Next.js where the Static Shell stops and the Dynamic Hole begins.
Next.js detects Dynamic APIs (cookies, headers, searchParams) inside the Suspense boundary and keeps the stream open only for that part.
Architecture visualization
Concept: Static Shell + Dynamic Holes
The "Static Shell + Dynamic Holes" Model
Concept introduced in Next.js 14
Immediate TTFB
The green parts are served from the global CDN Edge instantly. The user sees the layout and navigation in milliseconds.
Suspense-Driven Streaming
The purple parts are kept in the dynamic state on the server. As soon as the DB query finishes, the server streams the content.
Live PPR Playground
This storefront demonstrates the instant static load (Left) followed by the dynamic personalized stream (Bottom Right).
Static filters
Architecture Goal: Static Shell
Build fallback: Dynamic SSR
Network Reality:Even with a static shell, your Cache-Control will be no-store. Next.js does this to ensure the dynamic stream isn't cached by intermediate layers.
Pattern: PPR Shell (Defaults to Dynamic in Build)
Essence Mascara Lash Princess
The Essence Mascara Lash Princess is a popular mascara known for its volumizing and lengthening effects. Achieve dramatic lashes with this long-lasting and cruelty-free formula.
Eyeshadow Palette with Mirror
The Eyeshadow Palette with Mirror offers a versatile range of eyeshadow shades for creating stunning eye looks. With a built-in mirror, it's convenient for on-the-go makeup application.
Powder Canister
The Powder Canister is a finely milled setting powder designed to set makeup and control shine. With a lightweight and translucent formula, it provides a smooth and matte finish.
Red Lipstick
The Red Lipstick is a classic and bold choice for adding a pop of color to your lips. With a creamy and pigmented formula, it provides a vibrant and long-lasting finish.
This hole fetches data Dynamically. In an ideal PPR build, only this part streams.
Notice how the top section loads instantly while this section stays in "Skeleton" mode for 2.5s.
The "Shell" Paradox
You discovered the most confusing part of Next.js performance:"If we don't know what the user will click, how can the navigation be static?"
1. Static at BUILD Time
Next.js renders your Structural CSS and HTML (the sidebar frame, the nav icons, the branding) into a physical file during the build. This doesn't include the "Active Status" yet—just the Skeleton Architecture.
2. Served by CDN at Request
When a user navigates to your site (Initial Load), the CDN sends that Static Skeleton instantly (0ms). The browser renders the UI frame immediately. The server then streams the "Contextual Reality" (which link is active, user data) into that frame.
Client Persistence vs. CDN Shell
Standard React/Next.js Layouts already persist UI when you click links (Client Navigation).But PPR is for the FIRST time a user arrives. Without PPR, the server blocks everything until it computes which link should be active. With PPR, the structural shell lives on the CDN globally, delivered even before your server code starts running.
The Dynamic Triggers
Any of these APIs will force Next.js to switch this part of the page from Static to Streaming Dynamic:
cookies()Reading cookies for auth or personalization.
headers()Accessing request headers like user-agent.
searchParamsThe URL query string (e.g. ?search=next).
cache: 'no-store'Disabling caching for a specific fetch request.
revalidate: 0Forcing data to bypass the cache every time.
unstable_noStore()The explicit API to opt-out of static generation.
The Experimental Reality
Why use it if it's experimental?
PPR is the final piece of the architectural puzzle. While in "Experimental" mode, it allows engineering teams to benchmark the massive TTFB (Time to First Byte) improvementsbefore the feature goes stable. It is the roadmap for how high-scale Next.js apps will be built.
Wait, don't Layouts already persist?
Yes, Layouts keep the UI alive. But standard SSR still makes the browser wait for the server to generate that layout for every request. PPR allows the physical HTML fileof that layout to live on a CDN Edge, delivering it globally in 0ms.