View Transitions API 完全ガイド - ネイティブアプリのようなページ遷移をWebで実現

View Transitions API 完全ガイド - ネイティブアプリのようなページ遷移をWebで実現

作成日:
更新日:

View Transitions APIとは

View Transitions APIは、DOM更新時にスムーズなアニメーショントランジションを実現するためのWeb標準APIです。従来、ネイティブアプリでは当たり前だった「共有要素トランジション」や「ページ遷移アニメーション」を、Webでも簡単に実装できるようになりました。

従来の課題

Webでページ遷移やコンテンツ切り替えのアニメーションを実装する際、以下のような課題がありました:

課題詳細
複雑な実装変更前後の状態を手動で管理し、アニメーションを計算する必要がある
パフォーマンスDOM操作とアニメーションのタイミング調整が難しい
一貫性の欠如ページ間で統一されたトランジションを維持するのが困難
アクセシビリティトランジション中の状態管理が複雑

View Transitions APIは、これらの課題を解決します。

View Transitions API の仕組み

  1. スナップショット取得: DOM更新前の状態をキャプチャ
  2. DOM更新: コールバック内でDOMを更新
  3. 新しいスナップショット: 更新後の状態をキャプチャ
  4. アニメーション: 疑似要素を使って自動的にトランジション
更新前の状態 (old) → ::view-transition-old(root)
                            ↓ アニメーション
更新後の状態 (new) → ::view-transition-new(root)

基本的な使い方

document.startViewTransition()

View Transitions APIの中心は、document.startViewTransition()メソッドです。

JavaScript
// 基本的な使い方
document.startViewTransition(() => {
  // DOMを更新する処理
  updateTheDOM();
});

フォールバック付きの実装

JavaScript
function updateContent(newContent) {
  // View Transitions API がサポートされているか確認
  if (!document.startViewTransition) {
    // フォールバック: 通常のDOM更新
    renderContent(newContent);
    return;
  }
 
  // View Transitions を使用
  document.startViewTransition(() => {
    renderContent(newContent);
  });
}

ViewTransition オブジェクト

startViewTransition()はViewTransitionオブジェクトを返します。これを使って、トランジションの各段階を監視・制御できます。

JavaScript
const transition = document.startViewTransition(() => {
  updateDOM();
});
 
// 疑似要素が準備できた時
transition.ready.then(() => {
  console.log('アニメーション開始準備完了');
});
 
// トランジション完了時
transition.finished.then(() => {
  console.log('トランジション完了');
});
 
// トランジションをスキップ
transition.skipTransition();

デモ:基本的なView Transition

以下のデモでは、View Transitions APIの基本的な動作を確認できます。ボタンをクリックすると、カウンターの値や色がスムーズにトランジションします。

確認できる機能

  • カウンター: 数字の変更がフェードトランジションで表示
  • カラーチェンジ: 背景色がクロスフェードで切り替わる

基本的なView Transition

カウンター (数字がフェードで切り替わる)

0

カラーチェンジ (クロスフェード)

基本的な使い方
// View Transition を使用してDOM更新
document.startViewTransition(() => {
  // この中でDOMを更新
  updateTheDOM();
});

// フォールバック付き
if (document.startViewTransition) {
  document.startViewTransition(() => updateTheDOM());
} else {
  updateTheDOM(); // 通常の更新
}
ブラウザサポート状況
Chrome 111+
Edge 111+
Safari 18+
Firefox (flag)

このデモのポイントは、通常のDOM更新をView Transitionでラップするだけで、自動的にスムーズなアニメーションが適用されることです。

// View Transitionなし
setCount(count + 1);
 
// View Transitionあり
document.startViewTransition(() => {
  setCount(count + 1);
});

たったこれだけの違いで、ユーザー体験が大きく向上します。View Transitions APIは、ブラウザが自動的に変更前後のスナップショットを取得し、その間をアニメーションで補間してくれます。


CSSによるカスタマイズ

View Transitions APIは、CSSの疑似要素を通じてアニメーションをカスタマイズできます。

疑似要素ツリー

::view-transition
└─ ::view-transition-group(root)
   └─ ::view-transition-image-pair(root)
      ├─ ::view-transition-old(root)
      └─ ::view-transition-new(root)
疑似要素説明
::view-transitionトランジション全体のルート
::view-transition-group(name)特定の名前付きトランジショングループ
::view-transition-image-pair(name)old/newのペアを含むコンテナ
::view-transition-old(name)更新前のスナップショット
::view-transition-new(name)更新後のスナップショット

デフォルトアニメーションのカスタマイズ

CSS
/* トランジション全体の長さを変更 */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.5s;
}
 
/* フェードアウトをカスタマイズ */
::view-transition-old(root) {
  animation: fade-out 0.3s ease-out;
}
 
/* フェードインをカスタマイズ */
::view-transition-new(root) {
  animation: fade-in 0.3s ease-in;
}
 
@keyframes fade-out {
  to { opacity: 0; }
}
 
@keyframes fade-in {
  from { opacity: 0; }
}

スライドトランジション

CSS
/* 古いコンテンツを左にスライドアウト */
::view-transition-old(root) {
  animation: slide-out-left 0.4s ease-in-out;
}
 
/* 新しいコンテンツを右からスライドイン */
::view-transition-new(root) {
  animation: slide-in-right 0.4s ease-in-out;
}
 
@keyframes slide-out-left {
  to { transform: translateX(-100%); }
}
 
@keyframes slide-in-right {
  from { transform: translateX(100%); }
}

デモ:カスタムトランジション

以下のデモでは、CSSを使用してView Transitionのアニメーションをカスタマイズする方法を確認できます。複数のトランジションスタイルから選択して、違いを比較してみてください。

選択可能なトランジション

  1. フェード(デフォルト): 最もシンプルなクロスフェード
  2. スライド: 古いコンテンツが左に退場、新しいコンテンツが右から入場
  3. 円形展開: クリック位置を起点に円形に広がる
  4. フリップ: 3D回転でコンテンツが裏返る

カスタムトランジション

🌊

View A

クリックして切り替え

カスタムアニメーションの実装
/* CSS でトランジションをカスタマイズ */
::view-transition-old(custom-content) {
  animation: slide-out 0.4s ease-in-out;
}

::view-transition-new(custom-content) {
  animation: slide-in 0.4s ease-in-out;
}

@keyframes slide-out {
  to { transform: translateX(-100%); }
}

@keyframes slide-in {
  from { transform: translateX(100%); }
}
::view-transition-old()

トランジション開始時のスナップショット。 フェードアウトやスライドアウトに使用。

::view-transition-new()

トランジション後の新しいビュー。 フェードインやスライドインに使用。

カスタマイズのポイント

CSSの疑似要素::view-transition-old::view-transition-newに対してアニメーションを定義することで、トランジションの見た目を完全にコントロールできます。

円形展開トランジションは特に印象的で、ダークモード切り替えなどで人気のあるパターンです。これはtransition.readyプロミスとWeb Animations APIを組み合わせて実装しています。クリック位置を取得し、その位置からclip-path: circle()を使って円形に広がるアニメーションを適用します。


view-transition-name:共有要素トランジション

view-transition-nameCSSプロパティを使用すると、特定の要素に名前を付けて、その要素だけを個別にアニメーションさせることができます。これにより、「共有要素トランジション(Shared Element Transition)」が実現できます。

基本的な使い方

CSS
/* リスト内のカード */
.card {
  view-transition-name: card-1;
}
 
/* モーダル内の同じカード */
.modal-card {
  view-transition-name: card-1;
}

重要: view-transition-nameはページ内でユニークである必要があります。同じ名前が複数の要素に付いているとエラーになります。

動的な名前の割り当て

JSX
{items.map((item) => (
  <div
    key={item.id}
    style={{
      viewTransitionName: selectedId === item.id ? `card-${item.id}` : 'none'
    }}
  >
    {item.content}
  </div>
))}

デモ:カード展開トランジション

以下のデモでは、「共有要素トランジション(Shared Element Transition)」を確認できます。リスト内のカードをクリックすると、そのカードがスムーズに詳細ビューへと変化します。

共有要素トランジションとは

ネイティブアプリ(特にAndroid)ではお馴染みの機能で、画面間で同じ「意味」を持つ要素がスムーズに移動・変形するアニメーションです。例えば:

  • リストのサムネイル → 詳細画面のヘッダー画像
  • カードのタイトル → 詳細画面のタイトル
  • プロフィールアイコン → プロフィール画面のアバター

共有要素トランジション

カードをクリックすると、要素が拡大されてモーダルに変化します。view-transition-name で同一要素を識別します。

🏔️
Mountain View
🌅
Ocean Sunset
🌲
Forest Trail
共有要素トランジションの実装
// リスト内のカード
<div style={{ viewTransitionName: 'card-1' }}>
  カードの内容
</div>

// モーダル(同じ view-transition-name を指定)
<div style={{ viewTransitionName: 'card-1' }}>
  展開された内容
</div>

// トランジションを開始
document.startViewTransition(() => {
  setSelectedCard(id); // Reactの場合は状態更新
});
重要なポイント
  • view-transition-name は ページ内でユニークである必要があります
  • リストと詳細ビューで同じ名前を使うと、 ブラウザが自動的にトランジションを計算します
  • 複数の要素に同じ名前を付けるとエラーになります

実装のポイント

view-transition-nameは、トランジション前後で「同じ要素」であることをブラウザに伝えるための識別子です。同じ名前を持つ要素同士が自動的にペアリングされ、位置・サイズ・形状の変化がアニメーションで補間されます。

重要な注意点view-transition-nameはページ内でユニークである必要があります。リストで使用する場合は、選択されたアイテムのみに名前を付け、他のアイテムはnoneまたは未設定にしてください。


円形展開トランジション

transition.readyプロミスとWeb Animations APIを組み合わせることで、クリック位置を起点とした円形展開トランジションを実装できます。

実装コード

JavaScript
async function circularTransition(x, y, updateCallback) {
  // 最も遠いコーナーまでの距離を計算
  const endRadius = Math.hypot(
    Math.max(x, window.innerWidth - x),
    Math.max(y, window.innerHeight - y)
  );
 
  const transition = document.startViewTransition(() => {
    updateCallback();
  });
 
  // 疑似要素が準備できたら
  await transition.ready;
 
  // 新しいビューにアニメーションを適用
  document.documentElement.animate(
    {
      clipPath: [
        `circle(0px at ${x}px ${y}px)`,
        `circle(${endRadius}px at ${x}px ${y}px)`
      ]
    },
    {
      duration: 400,
      easing: 'ease-out',
      pseudoElement: '::view-transition-new(root)'
    }
  );
}

CSS側の設定

CSS
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}

デモ:テーマ切り替えトランジション

以下のデモでは、ダークモード/ライトモードの切り替えを円形展開トランジションで実装しています。トグルボタンをクリックすると、クリック位置を起点として円形にテーマが広がります。

円形展開トランジションの魅力

単純なフェードやスライドと比較して、円形展開は以下の点で優れています:

  • 視覚的なインパクト: ユーザーの注目を集める
  • 操作との関連性: クリック位置から広がるため、操作と結果の因果関係が明確
  • エレガントさ: ネイティブアプリのような洗練された印象を与える

テーマ切り替えトランジション

ボタンをクリックした位置から円形にテーマが切り替わります。transition.ready と Web Animations API を組み合わせています。

My App

ダークモード

View Transitions API を使用すると、テーマの切り替えを スムーズなアニメーションで表現できます。

Primary
Secondary
円形展開の実装
// トランジションを開始
const transition = document.startViewTransition(() => {
  setIsDark(!isDark);
});

// 疑似要素が準備できたらアニメーション適用
await transition.ready;

// クリック位置から円形に展開
document.documentElement.animate(
  {
    clipPath: [
      `circle(0px at ${x}px ${y}px)`,
      `circle(${endRadius}px at ${x}px ${y}px)`
    ]
  },
  {
    duration: 400,
    easing: 'ease-out',
    pseudoElement: '::view-transition-new(root)'
  }
);
transition.ready

疑似要素ツリーが作成されアニメーション可能になった時

transition.finished

アニメーションが完了し新しいビューが表示された時

transition.skipTransition()

トランジションをスキップして即座に完了

実装の仕組み

  1. transition.readyでスナップショットの準備が完了するのを待つ
  2. クリック位置から画面の最も遠いコーナーまでの距離を計算
  3. clip-path: circle()を使って、0pxから最大距離までの円を描く
  4. ::view-transition-new(root)疑似要素にアニメーションを適用

このパターンは、テーマ切り替え以外にも、モーダルの開閉やページ遷移など、様々な場面で応用できます。


リストのトランジション

リストの追加・削除・並べ替えにView Transitionsを適用すると、要素が滑らかに移動します。

実装のポイント

  1. 各アイテムに一意のview-transition-nameを設定
  2. 追加・削除・並べ替えをトランジションでラップ
JSX
{items.map((item) => (
  <div
    key={item.id}
    style={{ viewTransitionName: `item-${item.id}` }}
  >
    {item.content}
  </div>
))}
 
// 操作時
document.startViewTransition(() => {
  setItems(newItems);
});

デモ:リストトランジション

以下のデモでは、リストの追加・削除・並べ替え操作にView Transitionsを適用しています。各操作を実行すると、アイテムが滑らかに移動・出現・消失します。

操作可能なアクション

  • 追加: 新しいアイテムがフェードインで出現
  • 削除: アイテムがフェードアウトで消失
  • 上に移動: 選択したアイテムが1つ上の位置に滑らかに移動
  • シャッフル: すべてのアイテムがランダムな位置に移動

リストのトランジション

Task 1
Task 2
Task 3
リストアイテムのトランジション
// 各アイテムに一意の view-transition-name を設定
{items.map((item) => (
  <div
    key={item.id}
    style={{ viewTransitionName: `item-${item.id}` }}
  >
    {item.text}
  </div>
))}

// 追加・削除・並べ替えをトランジションでラップ
document.startViewTransition(() => {
  setItems([...items, newItem]); // 追加
  // or
  setItems(items.filter(i => i.id !== id)); // 削除
  // or
  setItems([...items].sort(() => Math.random() - 0.5)); // シャッフル
});
CSSでのカスタマイズ
/* 追加時のアニメーション */
::view-transition-new(list-item-*):only-child {
  animation: slide-in 0.3s ease-out;
}

/* 削除時のアニメーション */
::view-transition-old(list-item-*):only-child {
  animation: slide-out 0.3s ease-in;
}

/* 移動時のアニメーション */
::view-transition-group(list-item-*) {
  animation-duration: 0.3s;
}

従来の実装との比較

View Transitions APIを使わない場合、リストのアニメーションを実装するには以下のような手法が必要でした:

  • react-spring / Framer Motion: アニメーションライブラリを導入
  • FLIP技術: First, Last, Invert, Playの手動計算
  • CSS transitions + 状態管理: 複雑な状態管理ロジック

View Transitions APIを使えば、document.startViewTransition()でラップするだけで、これらの複雑な実装が不要になります。ブラウザが変更前後の要素の位置を自動的に計算し、補間アニメーションを生成してくれます。

各アイテムに一意のview-transition-nameを設定することがポイントです。これにより、ブラウザは「どの要素がどの要素に対応するか」を正確に認識できます。


ギャラリービューアー

画像ギャラリーでサムネイルをクリックすると詳細表示に遷移し、さらに左右にナビゲーションできるUIは、View Transitionsの典型的なユースケースです。

実装のポイント

  1. サムネイルとライトボックスで同じview-transition-nameを共有
  2. 選択されたアイテムのみに名前を付ける
  3. ナビゲーション時も名前を適切に更新
JSX
// グリッド内のサムネイル
<div
  style={{
    viewTransitionName: 
      selectedIndex === index ? `gallery-${item.id}` : undefined
  }}
>
  <img src={item.thumbnail} />
</div>
 
// ライトボックス
{selected && (
  <div
    style={{ viewTransitionName: `gallery-${selected.id}` }}
  >
    <img src={selected.fullsize} />
  </div>
)}

デモ:ギャラリービューア

以下のデモでは、画像ギャラリーでよく見られる「サムネイル → ライトボックス」の遷移をView Transitionsで実装しています。

操作方法

  1. グリッド内の画像をクリック → 詳細ビューが開く
  2. 左右の矢印 → 前後の画像に移動
  3. 背景または×ボタン → ライトボックスを閉じる

ギャラリービューア

画像をクリックして詳細表示。左右ナビゲーションでスムーズに切り替え。

🌸
🌻
🍁
❄️
🌊
ギャラリーの実装ポイント
サムネイル → 詳細
// 選択時のみ view-transition-name を設定
style={{
  viewTransitionName: 
    selectedIndex === index
      ? `gallery-${img.id}`
      : undefined
}}
画像間のナビゲーション
// 次/前の画像に切り替え
document.startViewTransition(() => {
  setSelectedIndex(
    (prev + 1) % images.length
  );
});
パフォーマンスのヒント
  • • 大量の画像がある場合、visible な要素のみに view-transition-name を設定
  • • 画像の遅延読み込みと組み合わせて最適化
  • • prefers-reduced-motion を尊重してアニメーションを制御

ギャラリービューアの実装ポイント

このパターンでは、サムネイルとライトボックスの画像が「同じ要素」として認識される必要があります。しかし、同時にview-transition-nameを持つことはできないため、工夫が必要です:

  1. 選択時のみ名前を設定: クリックされたサムネイルにのみview-transition-nameを付与
  2. ライトボックスも同じ名前: 表示される詳細画像にも同じ名前を設定
  3. ナビゲーション時の更新: 次/前の画像に移動する際、名前を適切に切り替え
// 選択されたアイテムのみに名前を付ける
style={{
  viewTransitionName: selectedIndex === index 
    ? `gallery-${item.id}` 
    : undefined
}}

このパターンをマスターすれば、ECサイトの商品一覧→詳細画面、SNSのフィード→投稿詳細など、様々なUIで応用できます。


クロスドキュメントトランジション(MPA対応)

View Transitions APIは、同一ドキュメント内(SPA)だけでなく、**異なるドキュメント間(MPA)**でもトランジションをサポートしています。これにより、従来のサーバーサイドレンダリングのWebサイトでもスムーズなページ遷移が可能になります。

有効化

CSS
@view-transition {
  navigation: auto;
}

この1行をCSSに追加するだけで、同一オリジン内のナビゲーションで自動的にView Transitionsが適用されます。

イベント

JavaScript
// ページを離れる時
window.addEventListener('pageswap', (event) => {
  const transition = event.viewTransition;
  // トランジションをカスタマイズ
});
 
// ページが表示される時
window.addEventListener('pagereveal', (event) => {
  const transition = event.viewTransition;
  // トランジションをカスタマイズ
});
JavaScript
window.addEventListener('pagereveal', (event) => {
  const navigation = performance.getEntriesByType('navigation')[0];
  const from = navigation.activationStart;
  
  // 遷移元のURLに基づいてトランジションをカスタマイズ
});

注意点

  • 同一オリジンのページ間でのみ動作
  • クロスオリジンリダイレクトがある場合は無効
  • 各ページで同じCSS設定が必要

React/Next.js での実装

基本的な実装

TypeScript
'use client';
 
import { useCallback } from 'react';
 
export function useViewTransition() {
  const startTransition = useCallback(
    (callback: () => void) => {
      if (!document.startViewTransition) {
        callback();
        return;
      }
      document.startViewTransition(callback);
    },
    []
  );
 
  return { startTransition };
}

使用例

TypeScript
function MyComponent() {
  const [view, setView] = useState('list');
  const { startTransition } = useViewTransition();
 
  const switchView = () => {
    startTransition(() => {
      setView(view === 'list' ? 'grid' : 'list');
    });
  };
 
  return (
    <div>
      <button onClick={switchView}>切り替え</button>
      {view === 'list' ? <ListView /> : <GridView />}
    </div>
  );
}

Next.js App Router での注意点

Next.jsのApp Routerでは、ページ遷移がクライアントサイドナビゲーションとして処理されるため、startViewTransitionを直接使用することはできません。カスタムのナビゲーションフックを作成するか、サードパーティのライブラリを使用する必要があります。

TypeScript
'use client';
 
import { useRouter } from 'next/navigation';
 
export function useViewTransitionRouter() {
  const router = useRouter();
 
  const navigate = (href: string) => {
    if (!document.startViewTransition) {
      router.push(href);
      return;
    }
 
    document.startViewTransition(() => {
      router.push(href);
    });
  };
 
  return { navigate };
}

ブラウザサポートとフォールバック

サポート状況(2026年1月時点)

ブラウザSPA (同一ドキュメント)MPA (クロスドキュメント)
Chrome111+ ✓126+ ✓
Edge111+ ✓126+ ✓
Safari18+ ✓18+ ✓
Firefox実験的サポート実験的サポート

フィーチャーディテクション

JavaScript
// SPA View Transitions
if (document.startViewTransition) {
  console.log('View Transitions API がサポートされています');
}
 
// MPA View Transitions
if (window.ViewTransition) {
  console.log('Cross-document View Transitions がサポートされています');
}

グレースフルデグラデーション

View Transitions APIは、未サポートブラウザでは単にトランジションなしでDOM更新が行われるため、プログレッシブエンハンスメントのアプローチで安全に使用できます。

JavaScript
function updateWithTransition(updateCallback) {
  if (document.startViewTransition) {
    document.startViewTransition(updateCallback);
  } else {
    updateCallback();
  }
}

パフォーマンスとアクセシビリティ

パフォーマンス最適化

  1. スナップショットの最小化: 大きな画像や複雑なDOMはスナップショット取得に時間がかかる
  2. contain プロパティ: contain: paintcontain: layoutで再描画範囲を限定
  3. トランジションのスキップ: 高速なナビゲーションではskipTransition()を検討
JavaScript
const transition = document.startViewTransition(() => {
  updateDOM();
});
 
// 500ms以内に次のアクションがあればスキップ
setTimeout(() => {
  transition.skipTransition();
}, 500);

アクセシビリティ

  1. prefers-reduced-motion の尊重
CSS
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 0.01ms !important;
  }
}
  1. フォーカス管理: トランジション後も適切な要素にフォーカスがあることを確認
JavaScript
const transition = document.startViewTransition(() => {
  updateDOM();
});
 
transition.finished.then(() => {
  document.querySelector('.main-content')?.focus();
});

まとめ

View Transitions APIは、Webアプリケーションのユーザー体験を大きく向上させる可能性を持つAPIです。

メリット

メリット詳細
シンプルな実装数行のコードでスムーズなトランジション
宣言的なカスタマイズCSSでアニメーションを定義
共有要素トランジションネイティブアプリのようなUI体験
MPA対応サーバーサイドレンダリングでも使用可能
プログレッシブエンハンスメント未サポートブラウザでも基本機能は動作

使いどころ

  • ページ遷移アニメーション
  • モーダル/ダイアログの開閉
  • リストから詳細ビューへの遷移
  • テーマ切り替え
  • タブ/ステップの切り替え
  • 画像ギャラリー

注意点

  • Firefoxは実験的サポートのため、プロダクションでは注意
  • view-transition-nameの一意性を保証する必要がある
  • 複雑なDOMでは パフォーマンスに注意
  • アクセシビリティ(reduced motion)への配慮

View Transitions APIを活用して、ユーザーを惹きつけるスムーズなWebアプリケーションを構築しましょう!


参考リンク