前言¶
單頁應用裏換路由,常見結果是舊頁面瞬間消失、新頁面瞬間出現。列表點進詳情時,縮略圖和頭圖明明是同一張圖,屏幕上卻看不出連續性。過去要補上這些過渡,往往得引入動畫庫,自己管掛載卸載、量位置、對時序。
瀏覽器後來提供了 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