mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
In #96074 I put the `partialFallback` behavior behind the `partialPrefetching` flag, so that we don't risk an explosion in ISR costs for apps that haven't opted into Partial Prefetching. But it turns out I only handled this for deploy mode — i.e. the build output consumed by the Vercel adapter. The behavior was still unconditionally on for `next start`. The reason it was easy to miss is that `next start` and the adapter express partial fallback shells through completely separate mechanisms. The adapter emits a `partialFallback` flag into the build output and lets the platform's ISR layer perform the shell upgrade. `next start` has no such flag; the server performs the upgrade itself, in the compiled page runtime. So the gate I added to the adapter output had no effect on the self-hosted path. To understand how the `next start` path was configured, I looked at the PR that removed the original top-level `partialFallbacks` config flag (#93859). Before that PR, the behavior was gated in the runtime; when the flag was removed those gates were deleted and the behavior became always-on. The important subtlety is that only the *upgrade* should be gated, not the shell machinery as a whole. On `next start`, the value that decides whether a shell can be specialized (`remainingPrerenderableParams`) also drives core Cache Components behavior — build-time partial prerendering of params and serving build-time sub-shells — which must stay on regardless of `partialPrefetching`. So this gates only the two things that make up the "upgrade on first request" cost risk: the background ISR revalidation that specializes a shell per request, and the client-facing `isFallbackUpgradeable` signal that tells the client to retry a prefetch waiting for that upgrade. Adds coverage with a `partialPrefetching`-disabled fixture asserting that fallback shells stay shared rather than specializing per request.
30 lines
563 B
TypeScript
30 lines
563 B
TypeScript
import { Suspense } from 'react'
|
|
|
|
async function Dynamic() {
|
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
return <div id="agent">Custom Data</div>
|
|
}
|
|
|
|
export async function generateStaticParams() {
|
|
return [{ slug: 'one' }]
|
|
}
|
|
|
|
export default async function Page({
|
|
params,
|
|
}: {
|
|
params: Promise<{ slug: string }>
|
|
}) {
|
|
const { slug } = await params
|
|
|
|
return (
|
|
<div>
|
|
<div id="slug" data-slug={slug}>
|
|
{slug}
|
|
</div>
|
|
<Suspense fallback={<div>Loading...</div>}>
|
|
<Dynamic />
|
|
</Suspense>
|
|
</div>
|
|
)
|
|
}
|