next.js
1d8e8381 - Replicate production prefetch shells for instant navigations in dev (#95067)

Commit
4 days ago
Replicate production prefetch shells for instant navigations in dev (#95067) The Instant Navigation lock makes a development render reflect the route's prefetched state: while the lock is held, a navigation shows only what was prefetched and keeps navigation-time data deferred. Both the `instant()` testing API from `@next/playwright` and the Instant Navigation devtools rely on it. The client side of that behavior, restricting the navigation read to the prefetched shell unless the link opts into a runtime prefetch and ignoring cache entries acquired before the lock, landed separately in #95150, which this change builds on. What remained were two ways the development app render diverged from what a production build serves, both in `app-page.ts`, so this PR contains no client-side router changes. While the lock is held, the app render now permits an empty static shell. A route that reads dynamic data such as `cookies()` outside any `<Suspense>` boundary has no static shell, so the on-demand render previously threw a static generation bailout, served an error page, and entered a `/_tree` redirect loop that committed the blocked data. Permitting an empty shell lets the render emit the shell instead. The override is scoped to the prefetch render made while the lock is held, a document request or a `'1'` static prefetch; a regular dynamic navigation, including the one that commits once the lock releases, runs without it. So the validation that flags a blocking route is not weakened: production validation runs at build time, and the development validation still runs and is shown in the dev overlay. The development fallback-shell render now reads the per-URL `fallbackParams` request meta that base-server derives for the requested URL, so the instant shell defers exactly the params a production prefetch would: `generateStaticParams`-covered params resolve in the shell and only the uncovered ones are deferred. When the URL is fully covered the meta is absent and nothing is deferred. That per-URL computation landed in #95066, which this change depends on. This suite now runs with App Shells enabled, which is the default under Cache Components. #94516 had temporarily forced it back to `false` here while the prefetch behavior settled and left the migration as a follow-up; re-enabling it is what lets these tests exercise the app-shell prefetch path the changes above target. New end-to-end coverage exercises the cases this render path affects. A deeper-segment blocking navigation must stay parked on the committed parent while the lock is held. Two mixed routes pair a `generateStaticParams`-covered param with an uncovered one: a plain route where a normal navigation surfaces only the covered param while the uncovered param and a request-time `connection()` sibling stay deferred, and an `allow-runtime` route where a `prefetch={true}` link additionally surfaces the uncovered param from the runtime prefetch. A blocking route that reads request data outside any `<Suspense>` boundary cannot be built for production, so it lives in a development-only fixture, guarded so the production job registers only a placeholder. The default fixture moves the dev-tools indicator to `bottom-right` so the Instant Navigation panel does not overlap the left-aligned test links during a navigation. One client-navigation cookie case is marked `it.failing`: on a non-partial route the speculative static prefetch is fuller than the app-shell render and supersedes it in the segment cache, so the cookie that only the app shell carries never reaches the instant shell. #95150's shell handling only engages under partial prefetching. Closing this needs a separate server-side change so that a route reading `cookies()` during app-shell generation opts into either partial prefetching, where only the app shell is fetched and nothing fuller can supersede it, or a runtime prefetch, where the speculative prefetch carries the cookie and no longer regresses what the app shell initially showed.
Author
Parents
Loading