React 19.3 が正式リリース
昨年experimentalとして公開された View Transitions と Fragment Refs が、React 19.3でstableに昇格しました。加えて、コンポーネント単位でサーバーレンダリングをスキップできる browser() API、Server ComponentからContextを直接レンダリングする機能も入っています。
今回のリリースは「新しい構文」というより、既存アプリのUXを一段引き上げるための道具立てが中心です。アニメーション、ローディング状態、ハイドレーションといった、これまで場当たり的に処理してきた領域をReactが公式APIとして取り込んだ、という位置づけになります。
本記事では、実務でそのまま使える4つの変更点に絞って解説します。
根拠資料: React 19.3 公式リリースノート

1. View Transitions — <ViewTransition> で囲むだけ
ブラウザの View Transition API を React コンポーネントとしてラップしたものです。enter / exit / update / share の4ケースを、Reactがツリーの変化から自動判定します。
import { ViewTransition, useState, startTransition } from 'react';
import { Video } from './Video';
export default function Component() {
const [showItem, setShowItem] = useState(false);
return (
<>
<button
onClick={() => {
// Transitionとしてマークしないとアニメーションが発火しません
startTransition(() => {
setShowItem((prev) => !prev);
});
}}
>
{showItem ? '➖' : '➕'}
</button>
{showItem && (
<ViewTransition>
<Video />
</ViewTransition>
)}
</>
);
}
注意点: startTransition、useDeferredValue、use() 経由の更新でないとアニメーションは動きません。通常の setState は urgent update として扱われ、即時反映されます。
addTransitionType でアニメーションを分岐
同じ state 変化でも「次のスライド」と「前のスライド」では別のアニメーションが必要です。そこで addTransitionType を使います。
function nextSlide() {
startTransition(() => {
addTransitionType('next'); // CSS側で :active-view-transition-type(next) として指定可能
setCurrentSlide((c) => c + 1);
});
}
function previousSlide() {
startTransition(() => {
addTransitionType('previous');
setCurrentSlide((c) => c - 1);
});
}
Suspense fallback のアニメーション(実務で最も有用)
Suspense 境界を <ViewTransition> で囲むと、fallback から実コンテンツへの切り替えをアニメーションできます。ただし 原則を守らないとUXが悪化 します。
- fallback はアニメーションなしで即時表示
- fallback → 最終コンテンツの切り替えはアニメーション
- suspend しない子はアニメーションなしで即時表示
これをコードに落とすと、update のみアニメーションを有効にする形になります。
<ViewTransition update="auto" enter="none" exit="none">
<Suspense fallback={<VideoPlaceholder />}>
<LazyVideo />
</Suspense>
</ViewTransition>
キャッシュ済みのUIにまでアニメーションが付くと、逆にアプリが重く感じられます。enter / exit は無効化しておくのが無難です。

2. Fragment Refs — wrapper div なしで子DOMを制御
<div> で囲まずに、子ノードへイベント・フォーカス・IntersectionObserver を適用できるようになりました。ライブラリのコンポーネントが ref を forward していなくても動作します。
import { useRef, useEffect } from 'react';
function Component({ posts }) {
const fragmentRef = useRef(null);
useEffect(() => {
const instance = fragmentRef.current;
instance.focus(); // 最初の子要素にフォーカス
}, []);
return (
<Fragment ref={fragmentRef}>
{posts.map((post) => (
<article key={post.id}>{post.title}</article>
))}
</Fragment>
);
}
FragmentInstance が提供するメソッドは以下の通りです。
addEventListener/removeEventListener/dispatchEvent— 第一階層の子要素のイベント管理focus/focusLast/blur— depth-first のフォーカス移動observeUsing/unobserveUsing— IntersectionObserver / ResizeObserver の接続getClientRects/getRootNode/compareDocumentPosition/scrollIntoView— 計測とスクロール
3. use(browser()) — サーバーレンダリングのスキップ
localStorage や Intl.DateTimeFormat().resolvedOptions().timeZone のように、ブラウザでのみ意味を持つ値を使うコンポーネントは、これまで useEffect + mounted state のような回避策が必要でした。19.3 からは公式APIが用意されています。
import { use } from 'react';
import { browser } from 'react-dom';
function TimeZone() {
use(browser()); // サーバーでは suspend、クライアントではそのまま続行
const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
return <span>{timeZone}</span>;
}
条件付きでサーバーレンダリングを opt-out することも可能です。たとえば initialData がある場合はサーバーでもレンダリングし、ない場合はブラウザのみでレンダリングするパターンです。
function useBrowserQuery(query, options) {
if (options.initialData === undefined) {
use(browser()); // 初期データがない場合はブラウザのみで実行
}
return useQuery(query, options);
}
このパターンは SSR フレームワークで loader の値を渡す場面で特に有用です。初期データがあれば HTML に含まれて SEO に有利、なければクライアントで fetch する、という分岐が自然に書けます。
4. Server Component から Context を直接レンダリング
これまでは 'use client' モジュール内に Provider ラッパーを別途用意する必要がありました。19.3 では Context オブジェクトをそのまま import し、<UserContext value={...}> の形で直接レンダリングできます。
// user-context.js
'use client';
import { createContext } from 'react';
export const UserContext = createContext(null);
// server-component.js
import { UserContext } from './user-context';
export async function Layout({ children }) {
const currentUser = await getCurrentUser();
return (
<UserContext value={currentUser}>
{children}
</UserContext>
);
}
この技術の限界と注意点
- View Transitions は DOM 専用 です。React Native 対応はまだ進行中で、RN で同じ挙動を期待してはいけません。
- ブラウザ対応にばらつき があります。View Transition API 自体がすべてのブラウザで完全ではないため、フォールバック CSS を用意しておくのが安全です。
- Suspense アニメーションの乱用は禁物。キャッシュ済みUIにまでアニメーションが付くと、体感パフォーマンスがむしろ落ちます。前述の3原則を必ず守ってください。
- Trusted Types は CSP ポリシーと併用 して初めて意味を持ちます。
require-trusted-types-for 'script'がなければ素通りします。
日本の開発現場での適用文脈
日本の受託開発やエンタープライズ案件では、まだ React 18 に留まっているケースも多く、19.3 の機能をすぐ導入できない環境もあるでしょう。ただし 自社サービスや新規プロジェクト では、browser() と View Transitions は今から検討する価値があります。特に管理画面ダッシュボードのように Suspense 境界が多い画面では効果が大きいです。
また、Qiita でも議論が活発な Next.js App Router 環境では、Server Component から Context を直接レンダリングできる変更のインパクトが大きく、これまで Provider ラッパーで生じていたボイラープレートをかなり削減できます。
次のステップ
- 公式ドキュメントの View Transitions ガイド を一読してください。
addTransitionTypeと CSS の:active-view-transition-type()の組み合わせを実際に試してみましょう。- Fragment Refs で既存のライブラリコンポーネントをラップするパターンを書いてみると、活用範囲が実感できます。
あわせて読みたい

まとめ
React 19.3 は「派手な新構文」よりも、実務UXを磨くためのツールセットという性格が強いリリースです。View Transitions でアニメーションを React ツリーの変化に自然に乗せ、Fragment Refs で DOM 構造を変えずに振る舞いを注入し、browser() で SSR 回避パターンを整理する — この3つを押さえるだけでも、今回のリリースの半分は活用できたと言えます。
特に Suspense + View Transitions の組み合わせは、管理画面・ダッシュボード・EC の詳細ページのように「ローディング → コンテンツ」の切り替えが多い画面で即効性があります。ただしアニメーションは 少なく、正確に が正解です。