レビュー待ち·難易度: 中級·更新: 2026-04-18

React.lazy と Suspense・ErrorBoundary を組み合わせてコード分割する

React.lazy でコンポーネントを遅延読み込みし、Suspense でローディング表示、ErrorBoundary で読み込み失敗時のフォールバックを実装する例。

nextjserror-handlingperformance

対応バージョン

nextjs 15react 19

前提環境

React の基本コンポーネント設計と dynamic import の概念を理解していること

概要

React.lazy でコンポーネントを動的インポートし、Suspense でローディング UI を表示、ErrorBoundary で読み込み失敗時のフォールバックを実装する。コード分割によるバンドルサイズ削減と、その際のエラーハンドリングをセットで示す。

インストール

# 追加インストールは不要

実装

ErrorBoundary クラスコンポーネント

// components/ErrorBoundary.tsx
import { Component, type ReactNode } from "react";

type Props = {
  fallback: ReactNode;
  children: ReactNode;
};

type State = { hasError: boolean; error: Error | null };

export class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback;
    }
    return this.props.children;
  }
}

遅延読み込みするコンポーネント

// components/HeavyChart.tsx
export function HeavyChart() {
  return (
    <div className="rounded border p-8 text-center text-sm text-gray-600">
      重いチャートコンポーネント(動的インポートされる)
    </div>
  );
}

React.lazy + Suspense + ErrorBoundary の組み合わせ

// app/page.tsx
"use client";

import { lazy, Suspense, useState } from "react";
import { ErrorBoundary } from "@/components/ErrorBoundary";

// React.lazy でコンポーネントを遅延読み込み
const HeavyChart = lazy(() => import("@/components/HeavyChart").then((m) => ({ default: m.HeavyChart })));

function LoadingFallback() {
  return (
    <div className="flex h-32 items-center justify-center rounded border border-dashed">
      <p className="animate-pulse text-sm text-gray-400">読み込み中...</p>
    </div>
  );
}

function ErrorFallback() {
  return (
    <div className="rounded border border-red-200 bg-red-50 p-6 text-center">
      <p className="text-sm text-red-600">コンポーネントの読み込みに失敗しました</p>
      <button
        onClick={() => window.location.reload()}
        className="mt-3 rounded bg-red-600 px-4 py-1.5 text-xs text-white hover:bg-red-700"
      >
        再読み込み
      </button>
    </div>
  );
}

export default function Page() {
  const [show, setShow] = useState(false);

  return (
    <main className="mx-auto max-w-xl p-8">
      <h1 className="mb-6 text-2xl font-bold">コード分割デモ</h1>

      <button
        onClick={() => setShow(true)}
        className="mb-6 rounded bg-blue-600 px-4 py-2 text-sm text-white hover:bg-blue-700"
      >
        チャートを表示
      </button>

      {show && (
        // ErrorBoundary で読み込み失敗を補足
        // Suspense でローディング UI を表示
        <ErrorBoundary fallback={<ErrorFallback />}>
          <Suspense fallback={<LoadingFallback />}>
            <HeavyChart />
          </Suspense>
        </ErrorBoundary>
      )}
    </main>
  );
}

複数コンポーネントを段階的に読み込む例

"use client";

import { lazy, Suspense } from "react";

const SectionA = lazy(() => import("@/components/SectionA").then((m) => ({ default: m.SectionA })));
const SectionB = lazy(() => import("@/components/SectionB").then((m) => ({ default: m.SectionB })));

export function PageContent() {
  return (
    <>
      {/* 各セクションが独立してローディングされる */}
      <Suspense fallback={<p className="text-sm text-gray-400">セクション A 読み込み中...</p>}>
        <SectionA />
      </Suspense>
      <Suspense fallback={<p className="text-sm text-gray-400">セクション B 読み込み中...</p>}>
        <SectionB />
      </Suspense>
    </>
  );
}

ポイント

  • React.lazy() => import(...) 形式の動的インポートを受け取る。名前付きエクスポートの場合は .then((m) => ({ default: m.ComponentName })) でデフォルトエクスポートに変換する
  • Suspenselazy コンポーネントのロード中に fallback を表示する。Suspense がないと React がエラーを投げる
  • ErrorBoundarySuspense の外側に置く。ネットワークエラー等でロードに失敗したとき fallback に切り替わる
  • nextjs-dynamic-import との違い: next/dynamic は Next.js 固有の SSR 対応 dynamic import。React.lazy は React 標準で Client Component 専用。CSR のみでよければ React.lazy で十分
  • react-error-boundary-component との違い: react-error-boundary-component は一般的なレンダリングエラーを捕捉する。このパターンは React.lazy のロード失敗に特化した組み合わせ

注意点

nextjs-error-boundary は App Router の error.tsx によるセグメントエラー処理。react-error-boundary-component はクラスベースのErrorBoundary手動実装。これは React.lazy + Suspense + ErrorBoundary の組み合わせによるコード分割パターン(Pages/App Router 共通)。

関連サンプル