feat(router): support View Transitions for Link navigation - #3208
Draft
NathanDrake2406 wants to merge 4 commits into
Draft
feat(router): support View Transitions for Link navigation#3208NathanDrake2406 wants to merge 4 commits into
NathanDrake2406 wants to merge 4 commits into
Conversation
Link navigation discards transition types, and settled prefetches bypass React transitions. Carry types to the approved visible commit and keep prefetched content in the transition lane when the feature is enabled. Require matching capable React runtimes for experimental.viewTransition. Pin the workspace React packages and types to 19.3.0, retain compatibility with older runtimes when disabled, and report native capability accurately. Cover real animations, cached navigation, missing browser support, and superseded responses in development and production browser tests.
Contributor
Author
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
commit: |
Contributor
Performance benchmarksCompared 0 improved · 5 regressed · 1 within ±1.5%
View detailed results and traces 🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head |
Keep strict CSP tests intact while accepting only the new React development eval diagnostic through an explicit per-test option. Continue to fail unexpected console errors and all uncaught page errors. Update Form navigation assertions for the optional transitionTypes argument. The dynamic-request test timeout did not reproduce locally.
Run the development fixture through the existing child-server helper so earlier production builds cannot leave a cached React production renderer paired with development JSX. Preserve every navigation assertion and the hydration timeout.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Closes #2603.
Why
React 19.3 makes View Transitions stable. On current main, Link accepts
transitionTypesbut discards it. The compatibility report still describes a no-op fallback, and settled prefetches force synchronous commits that prevent React animations.For example,
<Link transitionTypes={['slide']}>must select theslideanimation when its destination becomes visible. The navigation controller owns that commit. Registering types only in the Link click handler loses the React transition context across the asynchronous response.The change follows the existing scroll-intent and visible-commit-mode plumbing in
shims/navigation.tsandserver/app-browser-entry.ts. Fresh, prefetched, cached, redirected, and optimistic candidates carry the types toserver/app-browser-navigation-controller.ts.What changed
experimental.viewTransition: trueValidation
document.startViewTransition()calls and types, check console/hydration errors, verify cached/prefetched reuse without another request, and hold a response until its navigation is superseded.vp checkpassed repository-wide formatting, lint, and type checks.node scripts/sync-next-types.mjs --checkandnode scripts/check-shim-types.mjspassed.vp run vinext#buildpassed.pnpm install --frozen-lockfile --ignore-scriptspassed.dc23c9e25. The View Transition development fixture now uses the existing child-server helper. This prevents a preceding production build from mixing cached production React with development JSX. All navigation assertions and hydration timeouts remain unchanged.Review path and compatibility
utils/react-version.tsand the config define inindex.tsestablish the runtime requirement.server/app-browser-navigation-controller.tsowns type registration after approval.server/app-browser-entry.tspreserves types across candidates and keeps prefetched commits animatable.tests/e2e/app-router/view-transitions.browser.spec.tsproves browser behaviour.The feature defaults off. Existing React peer ranges and older-runtime compatibility shims remain available. The workspace version update affects all fixtures and examples; most lockfile changes are React peer-resolution keys.
The React migration cost is isolated with three local controls on the same machine and generated 33-route benchmark. Main is
13e7d9ce5(includes #3229). The React-only control changes onlypnpm-workspace.yamlandpnpm-lock.yaml. The full feature isb7975b54e.Each control used its frozen lockfile, the existing
benchmarks/generate-app.mjs, andbenchmarks/perf/run-scenarios.mjs --direct --rounds=3with Next.js excluded. The harness performs three clean production builds, then measures the final output three times. All three size reads matched. View Transitions remain off in this benchmark, matching CI. Withexperimental.viewTransition: true, the same full-feature benchmark emits 154,604 client bytes (+39 over React-only), 141,580 client-entry bytes (+35), 133,939 RSC-entry bytes (-3), and 227,314 server bytes (+3). These local byte counts isolate the dependency update; they do not establish runtime animation performance. Sequential local timing samples are not used to claim a speed change.The dependency update accounts for essentially all of the measured size increase. The final CI benchmark at
dc23c9e25reports production build time +1.6% and cold-start time -0.4% for the full change; those timings are not isolated by these size controls. Its acceptance remains a separate maintainer decision.Deploy compatibility was run against Next.js
v16.2.6, with the same manifest and zero assertion retries. Each control produced 799 result files covering the same 3,435 assertions.13e7d9ce5)0c5112a00)b7975b54e)Assertion-level comparison isolates twelve new failures to the React update, reproduced without any feature code:
app-action.test.tsandapp-action-node-middleware.test.ts, one global metadata-error assertion, and one process-taint assertion receive minified React error perf: og-inline-fetch-assets blocks Vite transform pipeline with sync I/O #441 instead of the expected production message.unstable_catchErrorServer Component recovery tests time out waiting for the error-boundary element, with and without React Compiler. These remain recovery failures; they are not dismissed as text-only changes.updateTagfetch-cache freshness). Single-run differences do not establish fixes.The full feature differs from the React-only control in one hash-scroll assertion (
navigation.test.ts, expected offset 2284, received 0). A focused navigation rerun atdc23c9e25passed all 50 assertions, including that hash-scroll assertion, with no retries. The extra failure did not reproduce. That head changes only the browser-test process isolation after the measured feature head; production code and dependencies are identical.Merge remains blocked on a deliberate React 19.3 compatibility decision. No deploy-suite expectations or support classifications were relaxed to hide these failures.
This implements the Link-specific contract in #2603.
router.push/replacetransition types remain a separate compatibility gap; this PR does not claim complete App Router View Transition parity.Non-goals: adding
router.push/replacetransition types, changing gesture navigation semantics, bundling a Canary React channel, or implementing a separate browser-level transition shim.References: React 19.3, Next.js View Transition fixtures.