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

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

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

小夜