cloudflare / cloudflare/vinext
App Router: support React ViewTransition animations and Link transitionTypes
- Dominant language
- TypeScript
- Stars
- 8.8k
- Forks
- 406
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 120
Description
## Summary
Add the React View Transition contract that Next.js exposes for App Router applications:
- accept `experimental.viewTransition`
- run React's `` and `addTransitionType` integration instead of a pass-through fallback
- support `` and associate those types with the navigation transition
Vinext rewrites `ViewTransition` and `addTransitionType` imports to `virtual:vinext-react-canary`. With the React version installed by this repo, that module returns children from `ViewTransition` and makes `addTransitionType` a no-op. `vinext check` reports the feature as partial and states that transitions will not animate.
Applications build without an import error. The fallback prevents `::view-transition-old(...)` and `::view-transition-new(...)` animations from running. `next/link` lacks the `transitionTypes` prop that Next uses to select transition-specific CSS.
## Existing work and scope boundary
Issue #1539 tracked gesture navigation and closed after #1909 made the Next.js gesture deploy test pass. PR #1909 added `experimental_gesturePush` and a synchronous approved-commit mode. Its non-goals include React Gesture Transition internals.
PR #2167 accepts `experimental.useExperimentalReact` and documents that vinext does not switch React release channels.
This issue tracks React `` animations and Link transition types. It does not reopen gesture navigation or replace `experimental_gesturePush`.
## Upstream contract
Next.js canary provides these pieces:
| Contract | Reference |
| --- | --- |
| `experimental.viewTransition` config | [config type and default](https://github.com/vercel/next.js/blob/canary/packages/next/src/server/config-shared.ts) |
| React `` App Router fixture | [view-transitions fixture](https://github.com/vercel/next.js/tree/canary/test/e2e/app-dir/view-transitions/fixtures/default) |
| `` | [App Router Link](https://github.com/vercel/next.js/blob/canary/packages/next/src/client/app-dir/link.tsx) |
| `addTransitionType()` inside the navigation transition | [App Router instance](https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/app-router-instance.ts) |
| Browser coverage | [view-transitions E2E](https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/view-transitions/view-transitions.test.ts) |
React documents the underlying APIs in [``](https://react.dev/reference/react/ViewTransition) and [`addTransitionType`](https://react.dev/reference/react/addTransitionType).
## Current vinext behavior
| Area | Behavior on `main` |
| --- | --- |
| React imports | [`vinext:react-canary`](https://github.com/cloudflare/vinext/blob/main/packages/vinext/src/index.ts) rewrites imports and supplies no-op fallbacks when React lacks the exports |
| Compatibility report | [`vinext check`](https://github.com/cloudflare/vinext/blob/main/packages/vinext/src/check.ts) reports `ViewTransition` as partial and says animations will not run |
| Next config | [`ExperimentalConfig`](https://github.com/cloudflare/vinext/blob/main/packages/vinext/src/config/next-config.ts) has no `viewTransition` option |
| Link API | [`next/link`](https://github.com/cloudflare/vinext/blob/main/packages/vinext/src/shims/link.tsx) has no `transitionTypes` prop or `addTransitionType` call |
## Reproduction
```tsx
// next.config.ts
export default {
experimental: {
viewTransition: true,
},
};
```
```tsx
import { ViewTransition } from "react";
import Link from "next/link";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
{children}
Next
);
}
```
With matching `::view-transition-old(.slide)` and `::view-transition-new(.slide)` rules, Next starts a typed transition. Vinext renders the new route without a View Transition animation.
## Implementation constraints
React, React DOM, RSC, SSR, and the browser bundle must use one compatible implementation. Mixing the stable and experimental channels across Vite environments can create invalid element types, hydration faults, or invalid hook calls.
Call `addTransitionType` inside the same `React.startTransition` that owns the App Router navigation. Keep the visual swap tied to vinext's approved visible-commit boundary so an aborted or superseded navigation cannot animate stale content.
Avoid wrapping the fetch alone in `document.startViewTransition()`. React's component API owns boundary naming, streamed commits, shared elements, and transition types.
## Suggested slices
1. Add typed config resolution for `experimental.viewTransition` and define its React capability requirements.
2. Provide compatible `ViewTransition` and `addTransitionType` exports across RSC, SSR, and browser environments.
3. Add `transitionTypes?: string[]` to App Router Link and register each type inside the navigation transition.
4. Port the Next.js fixture and add a browser assertion that observes `document.startViewTransition()` or an active view-transition pseudo-element animation.
## Acceptance criteria
- The upstream basic and `transitionTypes` fixtures build and navigate without console or hydration errors.
- Clicking a typed Link starts a browser View Transition and includes the requested transition type.
- A Link without `transitionTypes` uses React's default transition behavior.
- Browsers without `document.startViewTransition` still complete navigation.
- Superseded and aborted navigations do not animate an uncommitted route.
- `vinext check` no longer reports the feature as a pass-through fallback when the required runtime capability is active.
## Related
- #1539
- #1909
- #2109
- #2167
Contributor guide
Research direction
Start with the current fallback in packages/vinext/src/index.ts, the capability report in packages/vinext/src/check.ts, config types in packages/vinext/src/config/next-config.ts, and the Link shim in packages/vinext/src/shims/link.tsx. Compare these entry points with the referenced Next.js fixtures and App Router contracts, then run vinext check and the ported browser tests. Done means typed and untyped links navigate safely with the expected browser transition behavior, including aborted-navigation and unsupported-browser cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- next.js, react, typescript, vite
- Domain
- frontend, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100