cloudflare / cloudflare/vinext

App Router runtime render: include `partialPrefetching` in route renderOpts so initial HTML emits the `SubtreeHasPartialPrefetching` hint

Open
#1,938 0 comments 0 reactions 0 assignees View on GitHub
nextjs-tracking
Dominant language
TypeScript
Stars
8.8k
Forks
406
Avg merge
2d 6h
Merged PRs (30d)
120

Description

## Next.js Change

**Commit:** [`000e088`](https://github.com/vercel/next.js/commit/000e088059506a61c395f90a482938ee6419dcf7)
**PR:** [#94655](https://github.com/vercel/next.js/pull/94655)

## What changed

A one-line fix in `packages/next/src/build/templates/app-page.ts` that threads `nextConfig.partialPrefetching` into the `renderOpts` passed to the route module's runtime renderer:

```ts
renderOpts: {
// ...
cacheComponents: Boolean(nextConfig.cacheComponents),
partialPrefetching: nextConfig.partialPrefetching, // <-- added
validationLevel: nextConfig.experimental.instantInsights.validationLevel,
experimental: { /* ... */ },
}
```

The value is forwarded raw (not coerced to boolean) so the string `'unstable_eager'` survives.

## Why

Without this, `renderOpts.partialPrefetching` was `undefined` for every **runtime** render even when the flag was on in `next.config.ts`. Only build-time prerenders and per-segment prefetch artifacts (produced through the export worker, which has its own renderOpts assembly) had the correct value.

The visible bug: with `partialPrefetching` enabled, navigating back to a page that was first loaded as a **full HTML document** served stale dynamic content. Its `connection()`-gated output kept showing the value from the original document load instead of being re-run per request, and the `` navigation issued **no server request at all**. A page first reached through a prefetch behaved correctly, so the problem only surfaced after an initial HTML load.

The chain of events:

1. Runtime render of the initial HTML lacked `renderOpts.partialPrefetching`.
2. The route tree it computed and inlined into the HTML lacked the `SubtreeHasPartialPrefetching` hint and instead picked up `SubtreeHasEagerPrefetch`.
3. The client cached that route tree on first load.
4. On later `prefetch={true}` navigation, the client never downgraded the full prefetch to a partial one because the route tree didn't say to.
5. It promoted the fully resolved hydration entry out of the bfcache into a non-partial segment and served it stale.

The regression test navigates back to a page after its initial HTML load and asserts, via the router `act` helper, that the navigation re-fetches the dynamic content instead of serving it stale.

## Impact on vinext

This is directly relevant to vinext's tracking of partial prefetching support. Issues #1819 (config support), #1820 (Link prefetch default change), and #1917 (dev warning) cover the high-level surface. This commit is a parity fix on the **wiring path**: when partialPrefetching support lands in vinext, the same value must reach the runtime render that produces the initial HTML, not just the build-time prerender path.

Action items for the eventual implementation:

1. **One source of truth for the value.** Whatever vinext uses to assemble per-request renderOpts (the App Router server entry, or the equivalent of Next.js's `app-page.ts` template) must read `partialPrefetching` from the vinext config and pass it into the runtime render path, not just the build-time prerender path.
2. **Preserve the raw type.** Next.js intentionally does not coerce to boolean because `'unstable_eager'` is a valid value. If vinext exposes the same config option, it must pass through the raw string/boolean value, not normalize to boolean.
3. **The route tree must reflect the setting at render time.** The bug here was that the **runtime** render computed a route tree with the wrong prefetch-tree-hint bit because it didn't know about the setting. Whatever vinext does to derive client-side router state from server-side render output must read the same flag.
4. **Regression coverage.** The Next.js test pattern is the right shape: navigate to a page via HTML load, then navigate away, then `` back, and assert the dynamic content is re-fetched. Without the wiring, the navigation is silent and the content is stale — a regression test that only exercises the prefetched-first path will pass while the bug is present.

This is a low-effort but high-impact wiring detail. If vinext implements partialPrefetching support and misses this exact thread, the dev/test fixtures will mostly look fine (because they typically reach pages via prefetch) but apps will hit the bug as soon as any page is first reached via an HTML document load.

## Related

- #1819 — Support `partialPrefetching` global config and `unstable_prefetch = 'partial'` segment opt-in
- #1820 — Link `prefetch` default behavior change under Partial Prefetching: App Shell only
- #1917 — Dev warning: `` to non-partial-prefetch route under Cache Components
- #1614 — App Router: client-side App Shell prefetching

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.