Suspense × lazyで始めるコード分割とReactアプリの最適化
Reactで開発を行っていると、アプリケーションの成長とともに、初期表示のパフォーマンスやユーザー体験に関する課題が表面化してきます。
そのような課題の一つに「初期読み込みの遅さ」があります。
Reactでは、この問題を解決するための仕組みとして、React.lazy と Suspense が提供されています。
本記事では、これらを用いたコード分割の基本的な考え方と、実装方法について、初心者の方でも理解できるように、豊富なサンプルコードとともに解説いたします。
はじめに:コード分割とは?
コード分割(Code Splitting)とは、アプリケーション全体のJavaScriptコードを機能ごとに小さなチャンクに分けて、必要になったときだけ読み込む技術です。
メリット:
- 初期読み込みサイズが小さくなり、表示が速くなる
- ユーザーが実際に利用する機能だけを読み込めるため、無駄がない
Reactでは、この機能を実現するための標準的な手法として React.lazy と Suspense が提供されています。
参考:
基本的な使い方
1. React.lazy の基本構文
tsxconst LazyComponent = React.lazy(() => import('./MyComponent'));
このように書くことで、MyComponent は必要になるまで読み込まれない、遅延読み込みコンポーネントになります。
2. Suspense を使ったフォールバック表示
React.lazyで読み込んだコンポーネントは、読み込みが完了するまでに時間がかかるため、その間に表示する**フォールバック(代替表示)**を指定する必要があります。
tsximport React, { Suspense } from 'react';
const LazyComponent = React.lazy(() => import('./MyComponent'));
export default function App() {
return (
<Suspense fallback={<div>読み込み中...</div>}>
<LazyComponent />
</Suspense>
);
}
このように、<Suspense>でラップすることで、読み込み中に "読み込み中..." が表示され、コンポーネントが読み込まれた後に LazyComponent が描画されます。
よくある実装パターン
パターン1:ページごとに分割する
Next.jsでは自動でコード分割されますが、SPAの場合は手動で以下のように実装します。
tsxconst HomePage = React.lazy(() => import('./pages/Home'));
const AboutPage = React.lazy(() => import('./pages/About'));
function Router({ path }: { path: string }) {
return (
<Suspense fallback={<div>ページを読み込み中です...</div>}>
{path === '/' && <HomePage />}
{path === '/about' && <AboutPage />}
</Suspense>
);
}
パターン2:モーダルやダイアログなど、使用頻度の低いUIパーツを遅延読み込み
tsxconst LazyModal = React.lazy(() => import('./components/Modal'));
function App() {
const [isOpen, setIsOpen] = React.useState(false);
return (
<div>
<button onClick={() => setIsOpen(true)}>モーダルを開く</button>
{isOpen && (
<Suspense fallback={<div>モーダルを読み込み中...</div>}>
<LazyModal onClose={() => setIsOpen(false)} />
</Suspense>
)}
</div>
);
}
このようにすることで、実際に開かれるときに初めてモーダルを読み込むようになります。
注意点とベストプラクティス
1. Suspenseは単一コンポーネントに対して複数設置可能
必要な箇所ごとに <Suspense> を使うことで、読み込みの粒度を細かく制御できます。
tsx<Suspense fallback={<Loader1 />}>
<Component1 />
</Suspense>
<Suspense fallback={<Loader2 />}>
<Component2 />
</Suspense>
2. エラーハンドリングには ErrorBoundary を併用
React.lazyで読み込んだコンポーネントが失敗する場合のために、ErrorBoundaryを使って保険をかけておくのがベストです。
公式参考:
tsxclass ErrorBoundary extends React.Component<any, { hasError: boolean }> {
constructor(props: any) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <h1>エラーが発生しました。</h1>;
}
return this.props.children;
}
}
function App() {
return (
<ErrorBoundary>
<Suspense fallback={<div>読み込み中...</div>}>
<LazyComponent />
</Suspense>
</ErrorBoundary>
);
}
高度な活用:React Routerとの併用
React Router v6以降では、lazy() を使ってルートごとに遅延読み込みが可能です。
参考:
tsximport {
createBrowserRouter,
RouterProvider,
Route,
} from 'react-router-dom';
import { Suspense, lazy } from 'react';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const router = createBrowserRouter([
{
path: '/',
element: (
<Suspense fallback={<div>読み込み中...</div>}>
<Home />
</Suspense>
),
},
{
path: '/about',
element: (
<Suspense fallback={<div>読み込み中...</div>}>
<About />
</Suspense>
),
},
]);
function App() {
return <RouterProvider router={router} />;
}
まとめ
React.lazyとSuspenseを活用することで、Reactアプリの初期表示速度を改善できます。- フォールバックやエラー処理と組み合わせることで、より柔軟かつ安定したUXを実現できます。
- ページ単位の分割、モーダルなどのオンデマンドUIへの適用など、活用範囲は広いです。
React Routerなどルーティングライブラリとも自然に統合できます。
今後は React Server Components などとの併用も視野に入れることで、さらに効率的な開発が可能になります。
コード分割はパフォーマンス改善の第一歩です。ぜひ実際のプロジェクトに取り入れて、Reactのパフォーマンスを最大限に引き出してください。
article【緊急警告】React/Next.js の RSC 機能に CVSS 10.0 の RCE 脆弱性「CVE-2025-55182」が発覚
articleReact の最新動向まとめ:Server Components・並列レンダリング・エコシステム俯瞰
articleReact で管理画面を最短構築:テーブル・フィルタ・権限制御の実例
articleReact でデータ取得を最適化:TanStack Query 基礎からキャッシュ戦略まで実装
articleReact クリーンアーキテクチャ実践:UI・アプリ・ドメイン・データの責務分離
articleReact フック完全チートシート:useState から useTransition まで用途別早見表
articleCline で何が自動化できる?設計・実装・テスト・運用のユースケース地図
articleWeb Components で作るアクセシブルなタブ UI:キーボード操作& ARIA 完備
articleVue.js 可観測性:Sentry/OpenTelemetry/Web Vitals で UX を数値化
articleClaude Code SRE 実務:レート制限・キューイング・指数バックオフの実装指針
articleAnsible 実行モデル解体新書:コントローラからターゲットまでの裏側
articleACF かブロックか:WordPress 入力 UI の設計判断と移行戦略
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来