前言¶
单页应用里换路由,常见结果是旧页面瞬间消失、新页面瞬间出现。列表点进详情时,缩略图和头图明明是同一张图,屏幕上却看不出连续性。过去要补上这些过渡,往往得引入动画库,自己管挂载卸载、量位置、对时序。
浏览器后来提供了 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.md 的 name 字段是 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、触发时机。只有 startTransition、useDeferredValue 或 Suspense 会激活过渡。普通 setState 不会播动画。触发种类包括 enter(本次 Transition 中首次插入)、exit(首次移除)、update(边界内 DOM 变化,或因相邻兄弟导致自身尺寸/位置变化)、share(同名边界一个卸载、一个挂载)。share 优先于 enter / exit。
3、addTransitionType。在同一次 Transition 里打上类型标签,让不同边界按上下文选不同 CSS。可以连打多次。Next.js 16.2.0 起,next/link 和 useRouter().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 和带 transitionTypes 的 Link 可以写在 Server Component 里;router.push(..., { transitionTypes })、addTransitionType、startTransition 需要 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 之前需要);现在它不再承担这个作用。useSwipeTransition、parentEnter / parentExit 仍在 experimental channel,由 gestureTransition 等其它 flag 选用。若你用的是 Next.js 15,15 文档仍把该能力标为实验性,需要打开上述开关,当时的导入名是 unstable_ViewTransition。文档冲突时,以当前 Next.js 版本对应的官方指南为准。
2、不用 Next.js 的 React 项目。Skill 和 React 文档都写明:ViewTransition 不在稳定版 React 里,需要安装 react@canary 和 react-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/link 的 transitionTypes 时,用 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>
列表重排:每一项包一层带 key 的 ViewTransition,状态更新放进 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 ViewTransition:https://react.dev/reference/react/ViewTransition
Next.js 指南:https://nextjs.org/docs/app/guides/view-transitions