React 19.3は2026年9月9日に公開され、10月11日時点でnpmのlatestタグは19.3.0を指しています。目玉は、2025年に実験的APIとして紹介された<ViewTransition>とFragment refが安定版になったことです。ほかにSSRでブラウザ専用の描画をクライアントへ回すbrowser()、Trusted Types対応、Server ComponentsからのContext直接描画が加わりました。この記事ではReact公式ブログの19.3告知を正として、動かせるコード、19.2から上げるときに挙動が変わる箇所、新機能を入れない画面の条件をまとめました。19.2で入った<Activity>やuseEffectEventはReact 19.2の新機能|Activity・useEffectEvent・cacheSignal・PPRの使い方で扱っています。
まとめ:React 19.3で安定版になった機能と19.2から上げる前の確認4点
- 19.3.0は2026年9月9日公開。10月11日時点でパッチ版(19.3.1)は出ていません。
<ViewTransition>とFragment refが安定版になり、addTransitionType、browser()、Trusted Types対応、Server ComponentsでのContext直接描画が加わりました。- 公式ブログに破壊的変更の記載はありません。ただしTransitionの並列描画、hydration時のStrict Modeの二重実行など、動きが変わる変更は入っています。
<ViewTransition>はDOM専用です。React Nativeの画面には使えません。
React 19.3のリリース日と追加機能の一覧・npmで入っている版を確かめる手順
2026年9月9日公開の19.3.0とnpmのlatestタグで確かめる版番号
npm registryの公開日時を見ると、reactとreact-domの19.3.0はどちらも2026年9月9日の公開です。直前の19.2系は19.2.8(2026年7月21日)が最後で、型定義の@types/reactも同じ日に19.3.0が出ています。手元のプロジェクトでどの版が解決されているかは、次のコマンドで確かめられます。
npm view react version
npm ls react react-dom
npm view react time --json
1行目はレジストリの最新版、2行目はプロジェクトに実際に入っている版です。reactとreact-domの版がずれていると、react-dom 19.3.0はreactの^19.3.0を要求するため依存解決で警告が出ます。公式の版一覧はReact Versionsにもまとまっています。
安定版になった2つの機能と新規APIの収録パッケージを比べる対応表
19.3の追加分を、どのパッケージに入ったかと合わせて整理すると次のとおりです。
| 機能 | パッケージ | 19.3での扱い |
|---|---|---|
<ViewTransition> |
react | 実験版から安定版へ |
addTransitionType |
react | 遷移の向きを付ける |
| Fragment ref | react | 実験版から安定版へ |
browser() |
react-dom | 新規API |
| Trusted Types対応 | react-dom | 値を文字列化しない |
| Context直接描画 | react-server | Providerの部品が不要に |
browser()はreactではなくreact-domから読み込む点に注意してください。
ViewTransitionで要素の出現と消滅をアニメーションさせる実装手順
startTransitionで包んだ更新だけが動くenterとexitの基本形
<ViewTransition>は、ブラウザのView Transition APIを使って、子要素の出現(enter)、消滅(exit)、内容の変化(update)、名前付き要素の移動(share)をアニメーションさせる部品です。
import { ViewTransition, useState, startTransition } from 'react';
export default function Panel() {
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => startTransition(() => setOpen(v => !v))}>
{open ? '閉じる' : '開く'}
</button>
{open && (
<ViewTransition>
<section className="detail">詳細の内容</section>
</ViewTransition>
)}
</>
);
}
動くのはstartTransitionの中の更新、<Suspense>の表示切替、useDeferredValueによる更新の3種類だけです。onClickの中でsetOpenを直接呼ぶと、要素は即座に切り替わりアニメーションしません。既定の動きはクロスフェードで、入力への反応を優先する通常の更新を巻き込まない設計になっています。
addTransitionTypeで次へと前への向きを分けるカルーセル実装
同じsetStateでも、操作の理由で動きを変えたい場面があります。addTransitionTypeはTransitionに種類の名前を付け、enterやexitのpropsで種類ごとにCSSクラスを割り当てます。
import { ViewTransition, addTransitionType, startTransition } from 'react';
function next() {
startTransition(() => {
addTransitionType('next');
setIndex(i => i + 1);
});
}
<ViewTransition
key={slide.id}
enter={{ next: 'from-right', previous: 'from-left' }}
exit={{ next: 'to-left', previous: 'to-right' }}
>
<Slide slide={slide} />
</ViewTransition>
from-rightなどはCSS側で定義するクラス名です。Reactは種類の名前をブラウザのview transition typeとしても付けるため、CSSの:active-view-transition-type(next)で範囲を絞った書き方もできます。keyを変えて要素を差し替えることで、旧スライドのexitと新スライドのenterが同時に走ります。
Suspenseのfallbackから本体への切替だけを動かす設定例
データ取得と組み合わせると、読み込み表示から本来の内容へ切り替わる瞬間をアニメーションにできます。公式ブログが勧める型は「fallbackは即表示、fallbackから本体への切替だけ動かす、取得済みなら動かさない」の3原則です。
<ViewTransition update="auto" default="none">
<Suspense fallback={<CardSkeleton />}>
<ProfileCard userId={id} />
</Suspense>
</ViewTransition>
default="none"でenterとexitを止め、update="auto"で切替だけを残します。この指定を省くと、キャッシュ済みで一瞬で出る場面でもフェードが入るため、操作のたびに画面がもたつく印象になるのが難点です。Suspenseとuseでデータを読む書き方そのものは、Async Reactとは?Transition・Suspense・useOptimisticで非同期UIを組む実装で詳しく扱っています。
動かないときに疑うDOM要素との配置順序とnameの重複エラー
「書いたのにアニメーションしない」の原因で最も多いのは置き場所です。リファレンスのトラブルシューティングによると、enterとexitが動くのは<ViewTransition>がDOM要素より外側にあるときだけです。<div>の内側に置くと発動しません。
もう1つは共有要素用のnameです。同じnameの<ViewTransition>が同時にマウントされるとエラーになるため、一覧の各行に使うならname={`item-${id}`}のようにIDを含めて一意にします。prefers-reduced-motionをReactは自動で無効化しないので、CSSの@media (prefers-reduced-motion)でアニメーションを止める指定は自分で書いてください。ブラウザごとのView Transitions APIの対応状況はView Transitions APIの対応ブラウザと実装手順で確認できます。
Fragment refでラッパーのdivを足さずに複数の子要素を操作する方法
FragmentInstanceが持つイベント・フォーカス・監視・計測の4系統
19.3から<Fragment>にrefを渡せるようになり、ref.currentにはFragmentInstanceが入ります。これまで複数の兄弟要素をまとめて監視するには、ref用の<div>を足すしかなく、FlexboxやGridのレイアウトが崩れる原因になっていました。
- イベント:
addEventListener、removeEventListener、dispatchEvent(第1階層の子が対象) - フォーカス:
focus、focusLast、blur(入れ子の子を深さ優先でたどる) - 監視:
observeUsing、unobserveUsing(IntersectionObserverやResizeObserverを接続) - 計測:
getClientRects、getRootNode、compareDocumentPosition、scrollIntoView
外部ライブラリの部品がrefを受け取らない場合でも、Fragmentで包めば外側から振る舞いを足せます。単一要素へのrefの基本はuseRefの使い方|DOM参照・値の保持とuseStateとの違いを参照してください。
IntersectionObserverを接続して表示状態を返すInView部品の実装例
子要素のどれか1つでも画面に入ったらonChange(true)を呼ぶ部品は、次のように書けます。公式ブログの例を、実務で使いやすい形に短くしたものです。
import { Fragment, useRef, useLayoutEffect } from 'react';
export function InView({ onChange, children }) {
const ref = useRef(null);
useLayoutEffect(() => {
const visible = new Set();
const observer = new IntersectionObserver(entries => {
entries.forEach(e => e.isIntersecting ? visible.add(e.target) : visible.delete(e.target));
onChange(visible.size > 0);
});
const fragment = ref.current;
fragment.observeUsing(observer);
return () => fragment.unobserveUsing(observer);
}, [onChange]);
return <Fragment ref={ref}>{children}</Fragment>;
}
後片付けでunobserveUsingを呼ばないと、部品が消えたあとも監視が残ります。onChangeを親で毎回作り直すと購読も毎回やり直しになるため、親側でuseCallbackに包むか、React Compilerを入れている環境ならその自動メモ化に任せてください。
browser()でSSR中にブラウザ専用の描画をクライアントへ回す書き方
useEffectのmountedフラグを置き換えるuse(browser())の基本形
localStorageやブラウザのタイムゾーンに依存する部品は、サーバーで正しいHTMLを作れません。これまではuseEffectでmountedフラグを立てるか、typeof windowで分岐していました。後者はサーバーとクライアントでHTMLが食い違い、ハイドレーションエラーの典型的な原因になります。
import { Suspense, use } from 'react';
import { browser } from 'react-dom';
function TimeZone() {
use(browser());
const tz = new Intl.DateTimeFormat().resolvedOptions().timeZone;
return <p>{tz}</p>;
}
export default function App() {
return (
<Suspense fallback={<p>読み込み中…</p>}>
<TimeZone />
</Suspense>
);
}
サーバーではuse(browser())がSuspenseを起こし、最寄りの境界のfallbackがHTMLに入ります。ブラウザではsuspendせずにそのまま描画が進みます。通常のuseと同じく、条件分岐や早期returnの後でも呼べるので、「初期値が渡されていればサーバーで描き、無ければブラウザへ回す」という書き分けも可能です。useそのものの規則はReactのuseフック(use API)の使い方にまとめています。
Suspense境界が無いと失敗する制約とonBrowserBailoutでの記録
browserのリファレンスには、注意点が3つ書かれています。サーバー描画中は<Suspense>の内側で呼ぶこと(無いとサーバー描画が失敗する)、Client Componentから呼ぶこと、browser()を単独で呼んでも何も起きずuseに渡す必要があること、の3点です。
どの部品がクライアントへ回されたかは、サーバー描画APIのonBrowserBailoutで記録できます。browser()の引数に理由を渡すと、記録されるエラーのcauseに入ります。
const { pipe, abort } = renderToPipeableStream(<App />, {
onShellReady() {
pipe(res);
setTimeout(() => abort(browser('server render timed out')), 10000);
},
onBrowserBailout(error, errorInfo) {
console.warn(error.cause, errorInfo.componentStack);
},
});
abortにbrowser()の戻り値を渡すと、10秒で間に合わなかったSuspense境界をfallbackのまま送り、残りをブラウザで描かせられます。この経路ではonErrorもhydrateRootのonRecoverableErrorも呼ばれないため、監視をonErrorだけに頼っていると、クライアント描画へ落ちた件数が見えなくなります。
Trusted Types対応とサーバー側のContext直接描画の実装変更
CSP設定時にinnerHTMLへの型付きオブジェクト代入が通る理由
Trusted Typesは、DOMベースのXSSを防ぐブラウザの仕組みです。CSPのrequire-trusted-types-forで有効にすると、innerHTMLなどへ生の文字列を入れることが禁止され、ポリシーを通して作ったTrustedHTMLなどの型付きオブジェクトだけが受け付けられます。仕様はW3CのTrusted Types仕様にあります。
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types app-html
const policy = trustedTypes.createPolicy('app-html', {
createHTML: input => DOMPurify.sanitize(input),
});
<div dangerouslySetInnerHTML={{ __html: policy.createHTML(html) }} />
19.2までのReactは値を'' + valueで文字列に戻していたため、せっかく作ったTrustedHTMLがただの文字列になり、ブラウザに拒否されていました。19.3は値を文字列化せずに渡すので、ポリシーが意図どおり効きます。XSS対策全体の考え方はXSSとCSRFの違い|仕組み・対策を実装レベルで比較で整理しています。
Server ComponentからのContext直接描画で省ける部品
Server ComponentはContextを作れませんが、19.3からは'use client'のファイルで作ったContextを読み込んで、そのまま描画できます。これまではContextを渡すためだけのUserProvider部品を別に書いていました。
// user-context.js
'use client';
import { createContext } from 'react';
export const UserContext = createContext(null);
// layout.js(Server Component)
import { UserContext } from './user-context';
export async function Layout({ children }) {
const currentUser = await getCurrentUser();
return <UserContext value={currentUser}>{children}</UserContext>;
}
ログイン利用者などをクライアント側へ配るためだけのContextで、部品が1つ減ります。Contextの基本はReact Contextの使い方、サーバーとクライアントの境界の考え方はReact Server Components(RSC)とは?を参照してください。
19.2から19.3へ上げるときに挙動が変わる変更点と更新コマンド
npm installで上げるパッケージと@types/reactの版合わせ
reactとreact-domは必ず同時に上げます。TypeScriptの環境では@types/reactと@types/react-domも19.3系へそろえないと、<ViewTransition>やbrowserの型が見つからずエラーになります。
npm install react react-dom
npm install -D @types/react @types/react-dom
npm ls react react-dom @types/react
最後の行で、すべてが19.3.0に解決されたことを確かめてください。package.jsonの範囲指定が~19.2.0のように固定されている場合は、範囲を書き換えてからパッケージを入れ直してください。Next.jsなどのフレームワーク経由で使っている場合は、フレームワーク側でどの版に対応しているか、対応版の告知も合わせて確認する必要があります。Next.jsの直近の変更はNext.js 16.3とは?にまとめています。
Transitionの並列描画とSSR時のStrict Mode二重実行の影響
Changelogのうち、既存アプリの動きを変えうるのは次の2つです。
1つ目はTransitionの並列描画(PR #37290)です。これまで同時に進む複数のTransitionは1回の描画にまとめられていましたが、19.3では個別に描画されます。遅い画面遷移があっても、無関係な検索欄の絞り込みは、その画面遷移の完了を待つ必要がありません。その代わり、2つのTransitionの結果が同じ瞬間に画面へ出ることを前提にした実装は、間の状態が一瞬見える可能性があります。
2つ目はhydration時のStrict Modeの二重実行(PR #35961)です。SSRしたページでも、開発環境ではEffectが2回走るようになりました。クライアント描画のルートと同じ動きにそろえる変更で、本番の挙動は変わりません。ただし開発中に「SSRのページだけEffectが1回」を前提にしていたログ送信や計測コードは、19.3で重複して見えます。
number入力のdefaultValueなど細かな挙動変更の確認先
小さな変更では、type="number"の入力欄のdefaultValueの更新が他の入力型と同じ扱いになりました。フォームを再描画したときに初期値が書き換わるかどうかが変わるので、数値入力を多く持つ業務画面では一度動かして確かめてください。
ほかに、Server Actionによるフォームの自動リセットでonResetが発火する変更や、useDeferredValueが古い値のまま止まる不具合の修正が入りました。全件は公式ブログ末尾のChangelogで確認できます。
React 19.3の新機能を本番に採用する条件と見送る画面の判断基準
ViewTransitionを入れてよい画面と入れない画面の条件
ViewTransitionを入れてよいのは、一覧から詳細への遷移、タブやカルーセルの切替、Suspenseで遅れて出るカードなど、「どこからどこへ移ったか」を示すと操作が分かりやすくなる画面です。逆に、次の画面には入れないでください。
- 入力の手数が多い業務画面(受注入力、伝票登録など)。1回ごとの演出は短くても、1日に数百回繰り返す操作では待ち時間として積み上がる
- React Nativeと部品を共有している画面。DOM専用のため、ネイティブ側で同じ表示にならない
- ルーターが従来の
popstateで戻る操作を処理している画面。リファレンスによると、Reactはこの場合のアニメーションを飛ばすため、戻る操作だけ動かない不揃いな画面になる
判断の軸は「動きが情報を運ぶか」です。装飾だけのアニメーションなら入れません。
19.2系に留まる場合に失う修正と不具合対応で更新を急ぐべき構成
19.2系の最新は7月21日の19.2.8で、10月11日時点でそれ以降のパッチは出ていません。19.3に破壊的変更は無いため、更新の手間は小さい部類です。特に、CSPでTrusted Typesを強制したいサイト、useDeferredValueや<Activity>の不具合に当たっているアプリは、19.3の修正を取り込むのが現実的な解決手段になります。
一方、新APIを急いで入れる必要はありません。まず版だけを上げて既存の画面が変わらないことを確かめ、ViewTransitionやbrowser()は画面の改修のついでに部分ごとに入れる、という2段階が安全です。React 19全体の変更点はReact 19とは?18からの変更点・新機能で通して確認できます。版の更新と画面改修を同時に進める余力が社内に無い場合は、業務用・Webアプリ開発で、既存Reactアプリの更新計画から改修までを請け負っています。
React 19.3の新機能と既存アプリの更新についてのよくある質問
React 19.3の公開時期、互換性、新APIの使いどころについて、検索の多い質問に答えます。
React 19.3はいつリリースされましたか?最新版はいくつですか?
2026年9月9日に19.3.0が公開されました。npm registryの記録では、10月11日時点でreactとreact-domのlatestタグは19.3.0のままで、パッチ版は出ていません。npm view react versionで現在の最新版を確かめられます。
React 19.3に破壊的変更はありますか?
公式ブログに破壊的変更や非推奨の記載はありません。ただしTransitionの並列描画など、既存の動きが変わる項目はあるため、版を上げたら遷移の多い画面とSSRのページを一通り動かしてください。
ViewTransitionはReact Nativeでも使えますか?
使えません。React 19.3の<ViewTransition>はDOMでのみ動き、公式リファレンスはReact Nativeなど他の環境への対応を作業中としています。WebとReact Nativeで部品を共有している場合は、Web側だけで包むか、ネイティブ側は別のアニメーション手段で組む必要があります。
ViewTransitionは未対応のブラウザでどう動きますか?
未対応ブラウザでの挙動はReactのリファレンスに明記が無いため、対象ブラウザの実機で確かめるのが確実です。19.3ではMobile Safariで<ViewTransition>が落ちる不具合が直っており、19.3より前のCanary版で試していた場合は版を上げてから確認してください。
browser()とuseEffectのmountedフラグはどちらを使うべきですか?
19.3以降でSSRを使っているならuse(browser())を勧めます。mountedフラグは初回描画で一度空の表示を出し、その後に中身を差し替える仕組みなので、利用者に見える表示は2段階です。browser()はSuspenseのfallbackとして扱われるので、読み込み表示の出し方を他の非同期処理とそろえられます。ただしSuspense境界の内側で呼ぶことが条件です。
関連記事
- React 19.2の新機能|Activity・useEffectEvent・cacheSignal・PPRの使い方と19.2.8までの修正:19.3の1つ前の版で入った機能と修正
- Async Reactとは?Transition・Suspense・useOptimisticで非同期UIを組む実装【React 19.3】:ViewTransitionの前提になるTransitionとSuspenseの書き方
- View Transitions APIの対応ブラウザと実装手順|クロスドキュメント遷移まで:ReactのViewTransitionが使うブラウザAPIの仕組み
- React Server Components(RSC)とは?サーバー・クライアントの違いと使い分けを実装例で解説:Context直接描画とbrowser()の前提になる境界の考え方
- ハイドレーションエラー(Hydration Error)とは?原因とReact / Next.jsでの対処法を解説:browser()で避けられるSSRの食い違い