Reactでの「A React component suspended while rendering」エラーの解決方法

初級⚛️ React2026-07-05| React 16.6+, React 18 (Concurrent Mode), Next.js, Vite, Webpack

Error Message

Error: A React component suspended while rendering, but no fallback UI was specified. Add a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.
#react#suspense#javascriptエラー#パフォーマンス

シナリオ:コード分割がUIを壊すとき最近、メインのJavaScriptバンドルが2.5MBを超えて肥大化したダッシュボードを監査しました。軽量化のために、重いデータグリッドやD3チャートをオンデマンドの小さなチャンクに分割しようと、React.lazy()を採用しました。ローカル開発では軽快に動作していました。しかし、ステージング環境にデプロイして「Analytics」タブをクリックした瞬間、アプリケーション全体が消えてしまいました。代わりに表示されたのは真っ白な画面と、ブラウザコンソールに出力された大きなエラーでした。

Error: A React component suspended while rendering, but no fallback UI was specified.

このクラッシュは、Reactがまだ存在しないコンポーネントをレンダリングしようとしたために発生します。JSファイルがネットワーク経由でダウンロード中だったため、Reactは処理を継続できなくなりました。その数ミリ秒の空白時間に何を表示すべきか、Reactに指示が与えられていなかったのです。

なぜReactはこのエラーをスローするのかReactのエコシステムにおいて、「サスペンド(中断)」は一つのシグナルです。コンポーネントはレンダラーに対して、「ネットワークレスポンスや遅延ロードされたチャンクなどのリソースを待っているため、まだ準備ができていない」と伝えます。

React.lazy()を使用すると、動的インポート(dynamic import)が作成されます。そのファイルのダウンロードには、特に3G回線などでは時間がかかります。もし遅延ロードされるコンポーネントを<Suspense>境界で囲まなかった場合、Reactは不完全な、あるいは壊れたUIを表示することを拒否します。代わりに、状態の不整合を防ぐためにエラーをスローします。 以下のような場合にこのエラーに遭遇しやすくなります:

  • react-router-domを使用したルートベースのコード分割の実装時。- TanStack Query (React Query) で suspense: true フラグを有効にした時。- 実験的な use() フックを使用してレンダリング内で直接データを取得した時。## 解決策:Suspense境界の実装この修正は2分で終わります。問題のコンポーネントを<Suspense>タグで囲み、fallbackプロップを指定するだけです。このプロップは、メインコンテンツの読み込み中に何を表示すべきかをReactに明確に指示します。

例1:遅延ロードされるルートの修正もし App.js が以下のスニペットのようになっている場合、ユーザーがダッシュボードに移動した瞬間にクラッシュします:

import React, { lazy } from 'react';

const HeavyDashboard = lazy(() => import('./pages/HeavyDashboard'));

function App() {
  return (
    <div>
      <HeavyDashboard /> {/* ❌ これがエラーを引き起こします */}
    </div>
  );
}

これを修正するには、Suspenseコンポーネントを導入し、ローディング状態を定義します:

import React, { lazy, Suspense } from 'react';

const HeavyDashboard = lazy(() => import('./pages/HeavyDashboard'));

function App() {
  return (
    <div>
      <Suspense fallback={<div className="spinner">ダッシュボードを読み込み中...</div>}>
        <HeavyDashboard />
      </Suspense>
    </div>
  );
}

例2:きめ細かなフォールバック vs グローバルなフォールバックすべてのコンポーネントにラッパーが必要なわけではありません。一つの上位レベルの境界で、複数の遅延ロードされる子コンポーネントをキャッチできます。ただし注意点として、いずれかの子コンポーネントがサスペンドすると、ブロック全体がフォールバックに置き換わります。

<Suspense fallback={<GlobalLoader />}>
  <Header />
  <LazySidebar />
  <LazyMainContent />
</Suspense>

上の例では、サイドバーの読み込み中に Header も消えてしまいます。コンテンツの読み込み中もナビゲーションを表示し続けるには、より戦略的に境界をネストさせます:

<Header />
<div className="layout">
  <Suspense fallback={<SidebarSkeleton />}>
    <LazySidebar />
  </Suspense>
  <Suspense fallback={<MainSkeleton />}>
    <LazyMainContent />
  </Suspense>
</div>

検証:低速ネットワークでのテストローカル環境は高速すぎて、ローディング状態を確認できないことがあります。修正が機能しているか確認するために、Chrome DevToolsを使用して実環境をシミュレートしましょう:

  • F12を押して「ネットワーク(Network)」タブを開きます。- 「スロットリングなし(No throttling)」と表示されているドロップダウンを探します。- 「高速3G(Slow 3G)」を選択します(これでおよそ400msのレイテンシがシミュレートされます)。- ページを更新します。- コンポーネントが表示される前に、スケルトンスクリーンやスピナーが表示されることを確認します。## 本番環境向けのプロのアドバイス- fallbackプロップは必須です: たとえ何も表示したくない場合でも、fallback={null} を渡す必要があります。- ネットワークエラーの処理: Suspenseは「待機」のみを処理します。ユーザーのインターネット接続が切れ、JSチャンクのダウンロードに失敗した場合、Suspenseだけでは解決できません。404エラーやネットワークタイムアウトをキャッチするために、Suspense境界を ErrorBoundary で囲んでください。- バンドル解析: webpack-bundle-analyzer などのツールを使用して、React.lazy() によるインポートが実際に別ファイルとして作成されているか確認してください。

Related Error Notes