Partial Prerendering (PPR) is an experimental rendering strategy in Next.js that combines static prerendering (SSG/ISR) with dynamic streaming, allowing static and dynamic content to coexist in the same route without compromising performance
Partial Prerendering (PPR) represents a fundamental shift in how Next.js approaches rendering. Before PPR, a route had to be either entirely static (SSG/ISR) or entirely dynamic (SSR/streaming). If any component in a route needed dynamic data like cookies or headers, the entire route would switch to dynamic rendering, losing the performance benefits of static generation . PPR eliminates this trade-off by allowing developers to mark dynamic boundaries with Suspense, enabling Next.js to prerender a static shell at build time while leaving "holes" that are filled dynamically at request time . The static shell—including layouts, static components, and Suspense fallbacks—is served instantly from the edge, while dynamic components stream in parallel .
Build-time prerendering: Like SSG, PPR prerenders static content at build time. The layout, static components, and Suspense fallbacks become part of the cached static shell .
ISR compatibility: The static shell in PPR retains all the benefits of Incremental Static Regeneration—it can be updated via time-based revalidation, on-demand revalidation with tags/paths, or webhooks, just like traditional ISR .
Static by default: PPR follows the same philosophy as SSG—static optimization is on by default. Components remain static until they explicitly access dynamic APIs like cookies(), headers(), or searchParams .
Dynamic boundaries: Suspense boundaries mark where dynamic content begins. When a component inside Suspense uses dynamic APIs, only that boundary becomes dynamic while the surrounding shell stays static .
No more route-wide downgrade: This is the key improvement over traditional SSG—in the past, any dynamic component forced the entire route to become SSR; PPR confines the dynamic behavior to the component level .
At build time, Next.js prerenders a static shell for each route with PPR enabled. This shell includes the layout, all static components, and the fallback UI from Suspense boundaries . When a user visits the page, this shell is served instantly from the edge CDN. While the user is viewing the static content, the server renders the dynamic components in parallel and streams them to the client, where they replace the fallback UI . Crucially, this entire process happens in a single HTTP request—there's no extra round trip for the dynamic parts, which would defeat the performance benefits .
Dynamic APIs: Using cookies(), headers(), or reading searchParams inside a component makes it dynamic
Cache directives: Fetch requests with { cache: 'no-store' } or using noStore() in a component triggers dynamic rendering for that boundary
Request-time data: Any component that needs access to request-specific information must be wrapped in Suspense to become a dynamic boundary
Important: Wrapping a component in Suspense doesn't make it dynamic—the dynamic API usage inside does. Suspense is just the boundary marker
A significant limitation exists when combining PPR with dynamic routes that use ISR (on-demand generation). Currently, if you have a route like products/[slug] and you want to generate pages on-demand (not pre-rendering all slugs at build time), accessing params outside a Suspense boundary throws an error: "A component accessed data, headers, params, searchParams, or a short-lived cache without a Suspense boundary" . This forces developers to either pre-render all possible slugs (impractical for large catalogs) or wrap everything in Suspense, which defeats PPR's purpose by streaming all content dynamically . The Next.js team is actively working on improving this with concepts like Partial Fallback Prerendering (PFPR) .
Experimental feature: PPR is still experimental in Next.js 15 (as of early 2026) and not recommended for production use
Incremental adoption: Next.js 15 introduced the 'incremental' flag, allowing you to enable PPR route-by-route with experimental_ppr export
Next.js 16 evolution: In Next.js 16, PPR concepts are being integrated into a new "Cache Component" model, fundamentally changing how caching works
Feedback needed: The Next.js team encourages developers to try PPR in canary releases and provide feedback on GitHub
Documentation gaps: As of late 2025, many edge cases (like dynamic routes + ISR + PPR) are still being documented and refined
To address the dynamic route limitation, Next.js is developing Partial Fallback Prerendering (PFPR), which extends PPR to work with on-demand generated pages . PFPR allows even pages that weren't pre-rendered at build time to have a static shell. When a user requests a page that hasn't been generated yet, PFPR can serve a static shell immediately while generating the full page in the background, dramatically improving Time to First Byte (TTFB) for long-tail content . This feature is currently experimental and being integrated with hosting providers before becoming the default behavior .