I'm experimenting with Next.js 16's new Cache Components and Partial Prerendering (PPR) to build a product page that:
- Serves a static shell instantly (HTML for layout, header, footer)
- Streams the dynamic product data inside
<Suspense>
I expected the first request to generate the static shell + stream dynamic content, and subsequent requests to serve the full HTML from cache (if data hasn't changed).
But here's what's happening:
Code
// app/products/[id]/page.tsx
import { getProduct } from '@/lib/products';
import { Suspense } from 'react';
export const revalidate = 3600; // 1 hour
export default async function ProductPage({ params }: { params: { id: string } }) {
return (
<div>
<header>Header — Static</header>
<Suspense fallback={<p>Loading product...</p>}>
<ProductData id={params.id} />
</Suspense>
<footer>Footer — Static</footer>
</div>
);
}
async function ProductData({ id }: { id: string }) {
'use cache'; // Enable Cache Components
const product = await getProduct(id); // Should be cached
return (
<section>
<h1>{product.name}</h1>
<p>{product.description}</p>
</section>
);
}
// lib/products.ts
import { cache } from 'react';
export const getProduct = cache(async (id: string) => {
console.log('Fetching product:', id);
// Simulate API delay
await new Promise(r => setTimeout(r, 2000));
return {
id,
name: `Product ${id}`,
description: 'Lorem ipsum...',
};
});
Actual Behavior
First request: Works perfectly — shell loads instantly, product streams after 2s
Second request (within 1 hour):
- Shell loads instantly (good)
- But "Loading product..." fallback flashes for ~100ms, then product appears
- Console: Fetching product:123 logs again
Why is the dynamic segment not cached and served instantly on subsequent requests?
What I’ve Tried
cacheLife('1h')on the function → no effectexport const dynamic = 'force-dynamic'removedVerified
next.config.tshas:experimental: { ppr: true, cacheComponents: true }
Using
next start(production mode)No
cookies(),headers(), or auth in the chain
Questions
- Does PPR + Cache Components only cache the static shell, not the dynamic content?
- How can I make the entire page (shell + dynamic data) cacheable and instant on repeat visits?
- Is there a way to pre-warm the dynamic segment into the PPR cache?
Versions
Next.js: 16.0.0
React: 19.0.0
Node: 20.x
Any insight into how PPR and Cache Components interact would be amazing!
<ProductData />instead ofrevalidateAs far as I knowrevalidateis for the whole path so you could be revalidating the path and setting the cache component's return to stale so it refetches, showing you the loading indicator. TrycacheLifedirectly under"use cache"!