vercel-react-view-transitions について
React アプリ開発者向けに、ブラウザー標準の View Transitions で UI 状態間をアニメーションする方法を案内するスキルです。共有要素、リスト変更、Suspense、ルート遷移を扱い、適切な演出やアクセシビリティ、非対応ブラウザーでの動作も説明します。
主な機能
- 01ブラウザー標準 View Transitions
- 02共有要素とリストのアニメーション
- 03Suspense とルート遷移のパターン
- 04遷移タイプ別の CSS アニメーション
- 05アクセシビリティとブラウザー対応の指針
活用シーン
- 01リストから詳細画面への連続性の表現
- 02Suspense 後のコンテンツ表示の演出
- 03リスト並べ替え時の項目識別
- 04既存アプリの遷移実装の点検
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ julianromli/ ai-skills --skill vercel-react-view-transitionsインストール後、Claude Code で/vercel-react-view-transitionsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdmetadata.jsonREADME.mdreferences/css-recipes.mdimplementation.mdnextjs.mdpatterns.md詳細
React View Transitions
ブラウザー標準の document.startViewTransition を使って、UI の状態間をアニメーションさせます。<ViewTransition> で 何を 宣言し、startTransition / useDeferredValue / Suspense で いつ 開始するかを指定し、CSS クラスで どのように 表現するかを制御します。サポートされていないブラウザーでは、アニメーションを適切にスキップします。
アニメーションを適用する場面
すべての <ViewTransition> は、空間的な関係または連続性を伝えるものである必要があります。何を伝えるのか説明できない場合は、追加しないでください。
次のリストに該当するパターンをすべて、記載順に実装します。
| 優先度 | パターン | 伝える内容 |
|---|---|---|
| 1 | 共有要素(name) | 「同じものを、さらに深く見る」 |
| 2 | Suspense による表示 | 「データを読み込みました」 |
| 3 | リストの識別性(項目ごとの key) | 「同じ項目を、別の並びにする」 |
| 4 | 状態の変化(enter/exit) | 「何かが表示された/消えた」 |
| 5 | ルートの変更(レイアウトレベル) | 「新しい場所へ移動する」 |
これは実装の順序であり、「どれか一つを選ぶ」リストではありません。アプリに当てはまるパターンはすべて実装します。アプリにユースケースがない場合に限り、そのパターンをスキップしてください。
アニメーションスタイルの選択
| コンテキスト | アニメーション | 理由 |
|---|---|---|
| 階層的なナビゲーション(リスト → 詳細) | 型に応じた nav-forward / nav-back | 空間的な奥行きを伝えます |
| 横方向のナビゲーション(タブ間) | <ViewTransition> のみ(フェード)または default="none" | 伝えるべき奥行きがありません |
| Suspense による表示 | enter/exit の文字列プロパティ | コンテンツが表示されます |
| 再検証/バックグラウンド更新 | default="none" | 目立たせる必要がないため、アニメーションは不要です |
方向性のあるスライドは、階層的なナビゲーション(リスト → 詳細)と順序のあるシーケンス(前後の写真、カルーセル、ページ分割された結果)に限定します。順序のあるシーケンスでは、方向が位置を伝えます。「次」は右からスライドし、「前」は左からスライドします。横方向または順序のないナビゲーション(タブ間)では、方向性のあるスライドを使わないでください。空間的な奥行きがあるかのように誤解させます。
利用可能な環境
- Next.js:
react@canaryはインストールしないでください。App Router には React の canary がすでに内部でバンドルされています。ViewTransitionは追加設定なしで動作します。npm ls reactに安定版のようなバージョンが表示される場合がありますが、これは想定どおりです。 - Next.js 以外:
react@canary react-dom@canaryをインストールします(安定版の React にはViewTransitionが含まれていません)。 - ブラウザーのサポート状況:Chromium 111 以降、Firefox 144 以降、Safari 18.2 以降。サポートされていないブラウザーでは、適切に機能を縮退します。
実装ワークフロー
既存のアプリにビュー遷移を追加する場合は、references/implementation.md の手順に沿って進めてください。まず監査から始め、省略しないでください。CSS レシピは references/css-recipes.md からグローバルスタイルシートにコピーしてください。独自のアニメーション CSS は書かないでください。
基本概念
<ViewTransition> コンポーネント
import { ViewTransition } from 'react';
<ViewTransition>
<Component />
</ViewTransition>
React は view-transition-name を自動的に一意の値に設定し、内部で document.startViewTransition を呼び出します。自分で startViewTransition を呼び出さないでください。
アニメーションのトリガー
| トリガー | 発生するタイミング |
|---|---|
| enter | Transition 中に <ViewTransition> が初めて挿入されたとき |
| exit | Transition 中に <ViewTransition> が初めて削除されたとき |
| update | <ViewTransition> 内部で DOM が変更されたとき。ネストされた VT がある場合、変更は最も内側の VT に適用されます |
| share | 名前付きの VT がアンマウントされ、同じ name を持つ別の VT が同じ Transition 中にマウントされたとき |
VT が有効になるのは startTransition、useDeferredValue、または Suspense のみです。通常の setState ではアニメーションしません。
配置に関する重要なルール
<ViewTransition> は、DOM ノードより前に置かれている場合にのみ、enter/exit を有効にします。
// Works
<ViewTransition enter="auto" exit="auto">
<div>Content</div>
</ViewTransition>
// Broken — div wraps the VT, suppressing enter/exit
<div>
<ViewTransition enter="auto" exit="auto">
<div>Content</div>
</ViewTransition>
</div>
View Transition クラスを使ったスタイリング
プロパティ
値には "auto"(ブラウザーによるクロスフェード)、"none"(無効)、"class-name"(カスタム CSS)、または型ごとのアニメーションを指定する { [type]: value } を指定できます。
<ViewTransition default="none" enter="slide-in" exit="slide-out" share="morph" />
default が "none" の場合、明示的に指定しない限り、すべてのトリガーがオフになります。
CSS 疑似要素
::view-transition-old(.class)— 退出時のスナップショット::view-transition-new(.class)— 入場時のスナップショット::view-transition-group(.class)— コンテナー::view-transition-image-pair(.class)— 新旧のペア
すぐに使えるアニメーションレシピについては、references/css-recipes.md を参照してください。
トランジションの種類
addTransitionType でトランジションに種類を設定すると、コンテキストに応じて異なるアニメーションを VT に選択させることができます。複数回呼び出して種類を重ねると、ツリー内の異なる VT が異なる種類に反応します。
startTransition(() => {
addTransitionType('nav-forward');
addTransitionType('select-item');
router.push('/detail/1');
});
オブジェクトを渡すと、種類を CSS クラスにマッピングできます。enter、exit、および share で機能します。
<ViewTransition
enter={{ 'nav-forward': 'slide-from-right', 'nav-back': 'slide-from-left', default: 'none' }}
exit={{ 'nav-forward': 'slide-to-left', 'nav-back': 'slide-to-right', default: 'none' }}
share={{ 'nav-forward': 'morph-forward', 'nav-back': 'morph-back', default: 'morph' }}
default="none"
>
<Page />
</ViewTransition>
enter と exit は対称である必要はありません。たとえば、フェードインしつつ、方向に応じてスライドアウトできます。
<ViewTransition
enter={{ 'nav-forward': 'fade-in', 'nav-back': 'fade-in', default: 'none' }}
exit={{ 'nav-forward': 'nav-forward', 'nav-back': 'nav-back', default: 'none' }}
default="none"
>
TypeScript: ViewTransitionClassPerType では、オブジェクトに default キーが必要です。
複数のページがあるアプリでは、種類をキーにした VT を再利用可能なラッパーとして切り出します。
export function DirectionalTransition({ children }: { children: React.ReactNode }) {
return (
<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"
>
{children}
</ViewTransition>
);
}
router.back() とブラウザーの戻るボタン
router.back() とブラウザーの戻る/進むボタンでは、ビュー・トランジションは発生しません(popstate は同期的で、startViewTransition と互換性がありません)。代わりに、明示的な URL を指定して router.push() を使います。
種類と Suspense
種類はナビゲーション中に利用できますが、その後の Suspense の表示時には利用できません(別のトランジションであり、種類がありません)。ページレベルの enter/exit には種類マップを使い、Suspense の表示時には単純な文字列の props を使います。
共有要素のトランジション
アンマウントされる VT とマウントされる VT の 2 つに同じ name を指定すると、共有要素のモーフィングが作成されます。
<ViewTransition name="hero-image">
<img src="/thumb.jpg" onClick={() => startTransition(() => onSelect())} />
</ViewTransition>
// On the other view — same name
<ViewTransition name="hero-image">
<img src="/full.jpg" />
</ViewTransition>
- 同じ
nameを持つ VT は、同時に 1 つだけマウントできます。一意な名前(photo-${id})を使ってください。再利用可能なコンポーネントには注意が必要です。名前付き VT を含むコンポーネントがモーダル/ポップオーバーとページの両方にレンダリングされると、同時に両方がマウントされ、モーフィングが正しく動作しません。名前を props で条件分岐するか、名前付き VT を共有コンポーネントから特定の利用箇所へ移動してください。 shareはenter/exitより優先されます。ナビゲーション経路ごとに動作を検討してください。一致するペアが形成されない場合(たとえば遷移先のページに同じ名前がない場合)、代わりにenter/exitが発生します。そのような経路で要素にフォールバックアニメーションが必要かどうか検討してください。- 共有モーフィングがあるページでは、フェードアウトする exit を使用しないでください。代わりに方向性のあるスライドを使います。
よくあるパターン
入場/退出
{show && (
<ViewTransition enter="fade-in" exit="fade-out"><Panel /></ViewTransition>
)}
リストの並べ替え
{items.map(item => (
<ViewTransition key={item.id}><ItemCard item={item} /></ViewTransition>
))}
startTransition の中でトリガーしてください。リストと VT の間にラッパーの <div> を置かないでください。
共有要素とリストのアイデンティティの組み合わせ
共有要素とリストのアイデンティティは、それぞれ独立した役割を持ちます。混同しないでください。リスト項目に共有要素(たとえば詳細ビューへモーフィングする画像)が含まれる場合は、入れ子になった <ViewTransition> の境界を 2 つ使います。
{items.map(item => (
<ViewTransition key={item.id}> {/* list identity */}
<Link href={`/items/${item.id}`}>
<ViewTransition name={`item-image-${item.id}`} share="morph"> {/* shared element */}
<Image src={item.image} />
</ViewTransition>
<p>{item.name}</p>
</Link>
</ViewTransition>
))}
外側の VT はリストの並べ替え/入場アニメーションを処理します。内側の VT はルートをまたぐ共有要素のモーフィングを処理します。どちらか一方でも欠けると、そのアニメーションは何も起こらないままになります。
key で再入場を強制する
<ViewTransition key={searchParams.toString()} enter="slide-up" default="none">
<ResultsGrid />
</ViewTransition>
注意: <Suspense> をラップしている場合、key を変更すると境界が再マウントされ、再取得が発生します。
Suspense のフォールバックからコンテンツへの切り替え
シンプルなクロスフェード:
<ViewTransition>
<Suspense fallback={<Skeleton />}><Content /></Suspense>
</ViewTransition>
方向性のある表示:
<Suspense fallback={<ViewTransition exit="slide-down"><Skeleton /></ViewTransition>}>
<ViewTransition enter="slide-up" default="none"><Content /></ViewTransition>
</Suspense>
その他のパターンについては、references/patterns.md を参照してください。
複数の VT の相互作用
トリガーに一致するすべての VT が、単一の document.startViewTransition 内で同時に発生します。異なるトランジション(ナビゲーションと、その後の Suspense の解決)に属する VT は競合しません。
default="none" を積極的に使う
これを指定しないと、Suspense の解決、useDeferredValue の更新、バックグラウンドでの再検証など、すべてのトランジションで、すべての VT にブラウザーのクロスフェードが発生します。常に default="none" を指定し、必要なトリガーだけを明示的に有効にしてください。
2 つのパターンの共存
パターン A — 方向性のあるスライド: 各ページでタイプをキーにした VT を使用し、ナビゲーション中に実行されます。 パターン B — Suspense による表示: シンプルな文字列プロパティを使用し、データの読み込み時に実行されます(タイプはありません)。
これらは異なるタイミングで実行されるため、共存できます。両方に default="none" を指定すると、相互干渉を防げます。enter は必ず exit と組み合わせてください。方向性のある VT はレイアウトではなく、ページコンポーネントに配置してください。
ネストされた VT の制限
親 VT が終了すると、その内部にネストされた VT は独自の enter/exit を実行しません。アニメーションするのは最も外側の VT のみです。現在、ページナビゲーション中に項目ごとにずらしたアニメーションを実行することはできません。実験的なオプトイン修正については、react#36135 を参照してください。
Next.js との統合
Next.js のセットアップ(experimental.viewTransition フラグ、next/link の transitionTypes プロパティ、App Router のパターン、Server Components)については、references/nextjs.md を参照してください。
アクセシビリティ
references/css-recipes.md の視覚効果を抑える CSS を、必ずグローバルスタイルシートに追加してください。
リファレンスファイル
references/implementation.md— 実装ワークフローをステップごとに説明します。references/patterns.md— パターン、アニメーションのタイミング、イベント API、トラブルシューティングについて説明します。references/css-recipes.md— すぐに使える CSS アニメーションのレシピです。references/nextjs.md— Next.js App Router のパターンと Server Component の詳細です。
コンパイル済みドキュメント全文
すべてのリファレンスファイルを展開した完全なガイドについては、AGENTS.md を参照してください。