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のパフォーマンスを最大限に引き出してください。
articleReact 開発環境の作り方:Vite + TypeScript + ESLint + Prettier 完全セットアップ
articleReact とは? 2025 年版の特徴・強み・実務活用を一気に理解する完全解説
articleESLint を Yarn + TypeScript + React でゼロから構築:Flat Config 完全手順(macOS)
article【徹底比較】Preact vs React 2025:バンドル・FPS・メモリ・DX を総合評価
article既存 React プロジェクトを Preact に移行する完全ロードマップ
articleReact 開発を加速する GitHub Copilot 活用レシピ 20 選
articleSvelte のコンパイル出力を読み解く:仮想 DOM なしで速い理由
articleTauri で Markdown エディタを作る:ライブプレビュー・拡張プラグイン対応
articleStorybook で“仕様が生きる”開発:ドキュメント駆動 UI の実践ロードマップ
articleshadcn/ui で B2B SaaS ダッシュボードを組む:権限別 UI と監査ログの見せ方
articleSolidJS の Control Flow コンポーネント大全:Show/For/Switch/ErrorBoundary を使い分け
articleRemix で管理画面テンプレ:表・フィルタ・CSV エクスポートの鉄板構成
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 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来