Files
Zack TannerandGitHub c986bcccf1 fix: preserve cache behavior for PPR fallback shells with root params (#88556)
When generating PPR fallback shells, a cache miss during warmup was
being treated as a dynamic hole, so the warmup render never filled new
cache entries. This caused root‑param fallback shells (e.g.
`/en/blog/[slug]`) to suspend even though root params were already
known.

This change always creates a fresh prerender RDC for warmup and copies
any seed hits into it. The final render then uses the warmed cache, so
fallback shells can reuse populated entries without short‑circuiting. On
the export side, we now choose a seed RDC by matching known params (e.g.
locale) so /en/... fallback shells don’t get seeded with /fr/... data.
(Previously, the RDC seed was "last-write-wins".

Fixes NAR-716
2026-01-21 08:45:07 -08:00

79 lines
2.0 KiB
TypeScript

import { Suspense } from 'react'
import { cookies } from 'next/headers'
import Link from 'next/link'
async function getBlogPost(locale: string, slug: string) {
'use cache'
await new Promise((resolve) => setTimeout(resolve, 700))
return {
title: `Blog Post: ${slug}`,
locale,
content: 'This content was fetched from the CMS...',
relatedPosts: ['Related Post 1', 'Related Post 2', 'Related Post 3'],
}
}
export default async function BlogPost({
params,
}: {
params: Promise<{ locale: string; slug: string }>
}) {
return (
<main>
<div id="static-blog-header">Blog Article</div>
<div id="static-reading-time">Estimated reading time: 5 minutes</div>
<Suspense fallback={<div id="blog-loading">Loading article...</div>}>
<BlogContent params={params} />
</Suspense>
<Suspense fallback={<div id="dynamic-loading">Loading comments...</div>}>
<DynamicComments />
</Suspense>
</main>
)
}
// This component depends on locale and slug, so it's in Suspense
async function BlogContent({
params,
}: {
params: Promise<{ locale: string; slug: string }>
}) {
const { locale, slug } = await params
const post = await getBlogPost(locale, slug)
return (
<>
<article id="blog-content">
<h1>{post.title}</h1>
<p>Locale: {post.locale}</p>
<p>{post.content}</p>
</article>
<aside id="related-posts">
<h2>Related Posts ({post.locale})</h2>
<ul>
{post.relatedPosts.map((title, i) => (
<li key={i}>
<Link href={`/en/blog/${i}`}>{title}</Link>
</li>
))}
</ul>
</aside>
</>
)
}
async function DynamicComments() {
const cookieStore = await cookies()
const user = cookieStore.get('user')?.value || 'anonymous'
return (
<section id="comments">
<h3>Comments</h3>
<p>Viewing as: {user}</p>
</section>
)
}
export function generateStaticParams() {
return [{ slug: 'hello-world' }, { slug: 'getting-started' }]
}