React の `<ViewTransition>` コンポーネントで宣言的にページ遷移アニメーションを追加する
`<ViewTransition>` コンポーネントは React の実験的なバージョンとして導入されました。これは View Transition API を 宣言的な方法で使用できるようにするものです。
<ViewTransition> コンポーネントは 2025 年 1 月現在実験的な機能です。将来にわたって API が変更される可能性があります。
React の実験的なバージョンとして <ViewTransition> コンポーネントが導入されました。これは View Transition API を宣言的な方法で使用できるようにするものです。
View Transition API はページを遷移する際に簡単にアニメーションを追加できる API です。単一ページアプリケーション(SPA)においては document.startViewTransition() メソッドを DOM が変更される前に呼び出すことでページ遷移アニメーションを追加できます。<ViewTransition> コンポーネントを使用はこの API を React で使用するための方法です。
<ViewTransition> コンポーネントを使用することでブラウザの標準的な機能を使用してページ遷移アニメーションを追加できるという利点があります。
<ViewTransition> コンポーネントの使用
<ViewTransition> コンポーネントは React の実験的な機能であるため、react@experimental パッケージをインストールする必要があります。package.json に以下のように記述します。
{
"dependencies": {
"react": "experimental",
"react-dom": "experimental"
}
}Next.js を使用している場合には 15.2.0-canary.6 以降のバージョンが必要です。さらに next.config.js に以下の設定を追加します。
const nextConfig = {
experimental: {
viewTransition: true,
},
}さらに ViewTransition は不安定な API であるため、unstable_ViewTransition という名前で import する必要があります。
import { unstable_ViewTransition as ViewTransition } from "react";基本的な使用方法は、以下のように条件により出し分けられるコンポーネントを <ViewTransition> コンポーネントでラップすることです。
<ViewTransition>{condition ? <ComponentA /> : <ComponentB />}</ViewTransition><ViewTransition> コンポーネントは直近の子コンポーネントに対してランダムな値で view-transition-name CSS プロパティを追加します。View Transition API では前後の DOM ノードで同じ view-transition-name が設定されている場合にアニメーションが適用されるます。
View Transition API によるアニメーションを適用する場合には condition の変更を startTransition 関数でラップする必要があります。
import { useState, startTransition, unstable_ViewTransition as ViewTransition } from "react";
export function App() {
const [page, setPage] = useState("A");
const changePage = (newPage: string) => {
startTransition(() => {
setPage(newPage);
});
};
return (
<>
<button onClick={() => changePage("A")}>A</button>
<button onClick={() => changePage("B")}>B</button>
<ViewTransition>{page === "A" ? <PageA /> : <PageB />}</ViewTransition>
</>
);
}実際に試してみると、ページ遷移時にアニメーションが適用されることがわかります。
適用するアニメーションをカスタマイズする
View Transition API はデフォルトでフェードイン/フェードアウトのアニメーションが適用されます。アニメーションをカスタマイズする場合には ::view-transition-old, ::view-transition-new という疑似要素を使用できます。それぞれページ遷移前の古いページ、ページ遷移後の新しいページに対して適用される疑似クラスです。
@keyframes slide-in {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
@keyframes slide-out {
from {
transform: translateX(0);
}
to {
transform: translateX(100%);
}
}
::view-transition-old(foo) {
animation: slide-out 0.3s ease-out;
}
::view-transition-new(foo) {
animation: slide-in 0.3s ease-out;
}::view-transition-old, ::view-transition-new にはそれぞれセレクター(ここでは foo)を指定します。このセレクターに指定する値は transition-name と一致する必要があります。<ViewTransition> コンポーネントをデフォルトで使用する場合にはランダムな transition-name が設定されるためこの CSS で対象を特定できません。
<ViewTransition> コンポーネントに name Props を指定することで子コンポーネントに対して固定の transition-name を設定できます。
<ViewTransition name="foo">
{page === "A" ? <PageA /> : <PageB />}
</ViewTransition>これにより ::view-transition-old(foo), ::view-transition-new(foo) が適用され、ページ遷移時にスライドアニメーションが適用されることがわかります。
<Suspense> への適用
<ViewTransition> コンポーネントは <Suspense> コンポーネントと組み合わせて使用できます。<Suspense> コンポーネントは非同期でデータを取得する際に使用されるコンポーネントです。<Suspense> コンポーネントで非同期データの取得を行う場合には、<ViewTransition> コンポーネントをラップすると、fallback で指定したコンポーネントとコンテンツの遷移時にアニメーションが適用されます。
import {
Suspense,
unstable_ViewTransition as ViewTransition,
use,
} from "react";
let cache = new Map();
export function fetchData(url: string) {
if (!cache.has(url)) {
cache.set(url, getData(url));
}
return cache.get(url);
}
async function getData(url: string) {
if (url === "todos") {
return await fetchTodos();
} else {
throw Error("Not implemented");
}
}
const fetchTodos = async () => {
await new Promise((resolve) => setTimeout(resolve, 1000));
const todos = [
{ id: 1, title: "Buy milk" },
{ id: 2, title: "Take out the trash" },
{ id: 3, title: "Wash dishes" },
];
return todos;
};
function PageA() {
const todos = use(fetchData("todos"));
return (
<div>
<h1>Page A</h1>
<ul>
{todos.map((todo: any) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</div>
);
}
const Skelton = () => {
return <div>Loading...</div>;
};
function App() {
return (
<>
<ViewTransition>
<Suspense fallback={<Skelton />}>
<PageA />
</Suspense>
</ViewTransition>
</>
);
}
export default App;まとめ
<ViewTransition>コンポーネントは View Transition API を React で使用するための実験的な機能<ViewTransition>コンポーネントでラップしたコンポーネントにランダムな値でview-transition-nameCSS プロパティが追加されるview-transition-nameの値を指定するためにはnameProps を使用する<ViewTransition>コンポーネントは<Suspense>コンポーネントと組み合わせて使用することができる
