vercel-react-view-transitions:按官方工作流给 React 加上原生过渡动画

前言

单页应用里换路由,常见结果是旧页面瞬间消失、新页面瞬间出现。列表点进详情时,缩略图和头图明明是同一张图,屏幕上却看不出连续性。过去要补上这些过渡,往往得引入动画库,自己管挂载卸载、量位置、对时序。

浏览器后来提供了 View Transitions API,用 document.startViewTransition 对两次 UI 状态做快照并插值。React 把它收进了核心:用 ViewTransition 组件声明要动画的边界,由框架在 Transition / Suspense 期间调用原生 API。这套接口目前仍在 React 的 Canary / Experimental 通道,触发条件、放置位置、default="none"、共享元素的 name 都容易写错;写错了通常不是报错,而是动画不播。

Vercel 把这些规则收成一份 Agent Skill:vercel-react-view-transitions。装进 Cursor、Claude Code、Codex 这类支持 SKILL.md 的工具后,Agent 可以按固定工作流给现有应用补过渡,而不是临时拼一段 CSS。Next.js 官方的 View Transitions 指南也指向同一份 Skill。

这是什么

vercel-react-view-transitions 收录在 vercel-labs/agent-skills,目录是 skills/react-view-transitions/SKILL.mdname 字段是 vercel-react-view-transitions,作者标注为 vercel,元数据版本 1.0.0,许可证 MIT。它遵循 Agent Skills 通用格式,因此 Cursor、Claude Code、Codex CLI 等支持该格式的工具都可以用。

它不是 npm 动画库,也不会在运行时替你播动画。它给 Agent 一套实现说明:什么时候该加过渡、ViewTransition 怎么放、用哪些 CSS,以及 Next.js App Router 里怎么接到 next/link

一句话定位:用浏览器原生 View Transitions,在 React 里做有空间含义的页面和组件过渡,不额外引入第三方动画库。

目录结构如下:

react-view-transitions/
├── SKILL.md
├── AGENTS.md
└── references/
    ├── implementation.md
    ├── patterns.md
    ├── nextjs.md
    └── css-recipes.md

SKILL.md 是始终加载的核心说明;细节按需读 references/AGENTS.md 是把参考文件展开后的完整文档。

核心能力

Skill 的原则是:每一处 ViewTransition 都要能说清它在传达什么空间关系或连续性。说不清就不要加。

实现时按下面的优先级把适用的模式都做上,不是五选一:

优先级 模式 传达的含义
1 共享元素(name 同一件东西,进入更深一层
2 Suspense 揭示 数据加载完成
3 列表身份(每项 key 还是这些项,只是排列变了
4 状态进出(enter / exit 有东西出现或消失
5 路由切换(页面级) 去了一个新地方

动画风格也有对应关系。层级导航(列表到详情)用带类型的 nav-forward / nav-back;横向切 Tab 用淡入淡出或 default="none",不要用方向滑动,以免暗示并不存在的前后层级;Suspense 揭示用 enter / exit 字符串;后台刷新用 default="none",保持安静。

围绕这些模式,Skill 覆盖的能力可以分成几块。

1、ViewTransition 组件。从 react 引入后包住要动画的树。React 会自动分配 view-transition-name,并在后台调用 document.startViewTransition。React 文档和这份 Skill 都写明:不要自己再调 startViewTransition,页面上若另有过渡在跑,React 会打断它。

2、触发时机。只有 startTransitionuseDeferredValueSuspense 会激活过渡。普通 setState 不会播动画。触发种类包括 enter(本次 Transition 中首次插入)、exit(首次移除)、update(边界内 DOM 变化,或因相邻兄弟导致自身尺寸/位置变化)、share(同名边界一个卸载、一个挂载)。share 优先于 enter / exit

3、addTransitionType。在同一次 Transition 里打上类型标签,让不同边界按上下文选不同 CSS。可以连打多次。Next.js 16.2.0 起,next/linkuseRouter().push() / replace() 提供 transitionTypes,不必再手写 onNavigate + startTransition + addTransitionType

4、View Transition Class 与 CSS 伪元素。enter / exit / update / share / default 可取 "auto""none"、自定义类名,或按类型映射的对象。对应伪元素是 ::view-transition-old::view-transition-new::view-transition-group::view-transition-image-pair。现成配方在 references/css-recipes.md,Skill 要求先整份拷进全局样式,不要自己现写一套时序。

5、Next.js App Router 集成。包括页面级(不要放 layout)的方向过渡、loading.tsx 作为隐式 Suspense、跨路由共享元素、同一动态段用 key + name + share 做交叉淡入。ViewTransition 和带 transitionTypesLink 可以写在 Server Component 里;router.push(..., { transitionTypes })addTransitionTypestartTransition 需要 Client Component。

安装与启用

官方安装入口有三处写到同一条命令:Skill 自带 README、skills.sh 页面,以及 Next.js 的 View Transitions 指南。

只装这一份:

npx skills add vercel-labs/agent-skills --skill vercel-react-view-transitions

也可以写成完整仓库地址:

npx skills add https://github.com/vercel-labs/agent-skills --skill vercel-react-view-transitions

整仓技能集一起装:

npx skills add vercel-labs/agent-skills

这是 Vercel 的 skills CLI。默认装到当前项目,加 -g 装到用户目录。可以用 -a cursor-a claude-code-a codex 指定工具。CLI 通常把内容放到 .agents/skills/,再按检测到的 Agent 建符号链接。各工具自己也会读下面这些目录。

Cursor 官方文档列出的加载位置:

  • 项目级:.agents/skills/.cursor/skills/
  • 用户级:~/.agents/skills/~/.cursor/skills/
  • 兼容:.claude/skills/.codex/skills/ 以及对应的家目录路径

Claude Code 官方文档:

  • 项目级:.claude/skills/<name>/SKILL.md
  • 用户级:~/.claude/skills/<name>/SKILL.md

Codex CLI 常见位置:

  • 项目级:.codex/skills/
  • 用户级:~/.codex/skills/

手动安装时,把整个 react-view-transitions 目录拷到对应路径,保证 SKILL.md 在技能根目录。Cursor 里也可以打开侧边栏 Customize → Skills 查看是否被发现;需要时用 /vercel-react-view-transitions 显式调用。

装的是给 Agent 看的说明书。应用能不能播动画,还取决于 React 通道和浏览器。

1、Next.js App Router。Next.js 16 起内置 React canary,不要再执行 npm install react@canary。当前 Next.js 文档(指南版本 16.3.1,2026-08-07 更新)写明:View Transitions 在 App Router 中无需额外配置即可使用。Skill 的 references/nextjs.md 仍建议写上:

// next.config.js
const nextConfig = {
  experimental: { viewTransition: true },
};
module.exports = nextConfig;

并注明:这个开关历史上用于把 React 切到 experimental channel(ViewTransition 进入 canary 之前需要);现在它不再承担这个作用。useSwipeTransitionparentEnter / parentExit 仍在 experimental channel,由 gestureTransition 等其它 flag 选用。若你用的是 Next.js 15,15 文档仍把该能力标为实验性,需要打开上述开关,当时的导入名是 unstable_ViewTransition。文档冲突时,以当前 Next.js 版本对应的官方指南为准。

2、不用 Next.js 的 React 项目。Skill 和 React 文档都写明:ViewTransition 不在稳定版 React 里,需要安装 react@canaryreact-dom@canary

3、浏览器。React 用的是 View Transitions 的 v2 对象形式以及 transition types、view-transition-class。Skill 给出的范围是 Chromium 125+、Firefox 144+、Safari 18.2+。Next.js 指南补充:部分动画在 Safari 上表现可能不同;不支持的浏览器功能正常,只是没有过渡。

典型用法

装好之后,Next.js 官方指南给的提示词是:

Add view transitions to this app using the vercel-react-view-transitions skill.

也可以指定某一种过渡,例如缩略图变形到头图、路由前进/后退滑动、同一路由内内容交叉淡入。Skill 要求 Agent 先按 references/implementation.md 做代码审计,不要跳过,再拷 CSS 配方、隔离持久元素、加方向过渡、Suspense 揭示和共享元素。

下面几段代码来自 Skill 原文和 Next.js 指南,可直接对照仓库里的写法。

共享元素:两个视图上的 ViewTransition 使用同一个 name,一次 Transition 里一个卸载、一个挂载,浏览器会在位置和尺寸之间做变形。

import { ViewTransition } from 'react';

<ViewTransition name="hero-image">
  <img src="/thumb.jpg" onClick={() => startTransition(() => onSelect())} />
</ViewTransition>

<ViewTransition name="hero-image">
  <img src="/full.jpg" />
</ViewTransition>

Next.js 里更常见的是列表缩略图和详情头图成对,并用 transitionTypes 标记前进:

<Link href={`/products/${product.id}`} transitionTypes={['nav-forward']}>
  <ViewTransition name={`product-${product.id}`}>
    <Image src={product.image} alt={product.name} width={400} height={300} />
  </ViewTransition>
</Link>

<ViewTransition name={`product-${product.id}`}>
  <Image src={product.image} alt={product.name} width={800} height={600} />
</ViewTransition>

name 必须全局唯一,例如 photo-${id}。同一 name 同一时刻只能挂载一个;可复用组件如果在弹层和页面里同时渲染,变形会失效。列表项上既要重排动画、又要跨路由变形时,Skill 要求套两层:外层用 key 管列表身份,内层用 name 管共享元素。

方向导航:在页面组件(不是 layout)上按类型映射进出场。Layout 在路由间保持挂载,enter / exit 不会在切页时触发。

import { ViewTransition } from 'react';

<ViewTransition
  enter={{ 'nav-forward': 'nav-forward', 'nav-back': 'nav-back', default: 'none' }}
  exit={{ 'nav-forward': 'nav-forward', 'nav-back': 'nav-back', default: 'none' }}
  default="none"
>
  <Page />
</ViewTransition>

没有 next/linktransitionTypes 时,用 addTransitionType

import { startTransition, addTransitionType } from 'react';

startTransition(() => {
  addTransitionType('nav-forward');
  addTransitionType('select-item');
  router.push('/detail/1');
});

Next.js 16.2.0 起,按钮导航可以写成:

'use client';
import { useRouter } from 'next/navigation';

function DetailButton({ href }: { href: string }) {
  const router = useRouter();
  return (
    <button onClick={() => router.push(href, { transitionTypes: ['nav-forward'] })}>
      Open
    </button>
  );
}

transitionTypes 只在 App Router 生效;Pages Router 会静默忽略,因此同一套链接组件可以两边共用。

Suspense 揭示要用字符串 props,不要用类型映射。Suspense 后续兑现是另一次 Transition,带不上导航时的 type。

<Suspense
  fallback={
    <ViewTransition exit="slide-down">
      <Skeleton />
    </ViewTransition>
  }
>
  <ViewTransition enter="slide-up" default="none">
    <AsyncContent />
  </ViewTransition>
</Suspense>

列表重排:每一项包一层带 keyViewTransition,状态更新放进 startTransition。列表和 VT 之间不要再套一层会截获 update 的包装 VT。

{items.map(item => (
  <ViewTransition key={item.id}>
    <ItemCard item={item} />
  </ViewTransition>
))}

进出场有一条放置规则:ViewTransition 必须出现在任何 DOM 节点之前,外面包一层 div 会压掉 enter / exit

<ViewTransition enter="auto" exit="auto">
  <div>Content</div>
</ViewTransition>

Vercel 提供了可运行的对照实现:演示源码。完整 CSS 可参考演示仓库里的 globals.css

适用场景与注意事项

适合这些情况:Next.js App Router 或已使用 React canary 的项目,要做列表到详情的共享元素、路由前进/后退、Suspense 骨架到内容、列表过滤重排,而又不想上第三方动画库。

不适合或需要降级的情况:稳定版 React(非 Next.js、也未装 canary);Pages Router(transitionTypes 无效);目标浏览器低于 Chromium 125 / Firefox 144 / Safari 18.2。不支持时应用仍可用,只是没有过渡。

下面这些限制来自 Skill 和 React / Next.js 文档,写错时通常是静默不播。

1、default="none" 要有意使用。裸 <ViewTransition> 会在每次导航、每次 Suspense 兑现、每次后台刷新时都走浏览器默认交叉淡入。命名共享元素和带类型的页面 VT 应加上 default="none",再显式打开需要的触发器。但 default="none" 也会关掉 update 和未声明的 share:列表项和被挤开的兄弟如果需要位移动画,应保持裸 VT 或 update="auto";共享元素在 default="none" 时必须显式写 share

2、router.back() 和浏览器前进/后退不带 transition types,方向滑动会落到对象里的 default(一般为 "none")而不播放;未按类型配置的共享元素变形仍可能生效。需要完整后退动画时,用 router.push() 带上明确 URL 和类型。

3、共享元素要求新旧视图在同一次 Transition 里都已渲染。目标页如果先 Suspend 到 fallback,配对失败,等数据到来时走另一次没有 nav-forward 的揭示。动态路由默认预取可能只有壳,需要完整内容时把 prefetch={true} 打开,并缓存共享元素所需数据。开发模式不会自动预取,方向过渡应在生产构建、客户端缓存为空时验证。

4、嵌套 VT:父级作为整体挂载/卸载时,内部 VT 不会各自走 enter / exit,只有最外层动。页面导航时的逐项交错目前做不到。parentEnter / parentExit 仍在 experimental channel。

5、不要给带共享变形的页面配淡出 exit,会和 morph 抢;改用方向滑动。不要用裸 viewTransitionName 样式去「触发」动画——那只是把持久元素(页头、侧栏)从页面快照里隔离出去;真正调用 startViewTransition 的是树上的 ViewTransition

6、无障碍。React 不会根据用户偏好自动关动画。Skill 和 React 文档都要求在全局样式里加上 prefers-reduced-motion。Next.js 指南给的一份写法是把持续时间打到 0:

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(*),
  ::view-transition-new(*),
  ::view-transition-group(*) {
    animation-duration: 0s !important;
    animation-delay: 0s !important;
  }
}

小结

vercel-react-view-transitions 把 React 仍在 Canary 通道里的 View Transitions,收成 Agent 可执行的审计和实施顺序:先决定每段导航在传达什么,再按共享元素、Suspense、列表、进出场、路由的顺序落地,并用官方 CSS 配方控制观感。对 Next.js App Router 项目,官方安装命令是 npx skills add vercel-labs/agent-skills --skill vercel-react-view-transitions,然后让 Agent 按这份 Skill 改现有应用。

Skill 原文:https://github.com/vercel-labs/agent-skills/tree/main/skills/react-view-transitions

技能集仓库:https://github.com/vercel-labs/agent-skills

React ViewTransitionhttps://react.dev/reference/react/ViewTransition

Next.js 指南:https://nextjs.org/docs/app/guides/view-transitions

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜