Next.js dynamic importの使い方完全ガイド|エラー解決とベストプラクティス
Next.jsアプリケーションを開発する際、バンドルサイズの最適化やページロード速度の改善は重要な課題です。その解決策として「dynamic import(動的インポート)」があります。しかし、正しく使用しないとエラーが発生する可能性があります。本記事では、Next.jsのdynamic importについて、初心者向けに詳しく解説します。
そもそも「dynamic import」とは何か?
dynamic importは、JavaScriptの標準機能として、必要な時点でモジュールを読み込む手法です。通常のインポートと異なり、条件付きやオンデマンドでコンポーネントやモジュールを読み込めます。
Next.jsでは、このdynamic importを拡張し、サーバーサイドレンダリング(SSR)環境でも安全に使用できるようにしています。
エラーが発生する主な原因
原因1:SSRとの不互換性
Next.jsでdynamic importを使う際の最大の問題は、サーバーサイドレンダリング時にエラーが発生することです。特にブラウザAPI(window、document など)に依存するコンポーネントを読み込もうとすると、サーバー環境でこれらのオブジェクトが存在しないため、エラーが起きます。
原因2:loading状態の未処理
dynamic importでコンポーネントを読み込む際、読み込み完了までの時間が必要です。この間にコンポーネントが表示されていないと、UIが不安定になります。
原因3:TypeScriptでの型定義不足
TypeScriptプロジェクトでdynamic importを使う場合、型情報が正しく設定されていないと、コンパイルエラーが発生します。
解決手順
ステップ1:dynamic関数をインポート
Next.jsのdynamic importを使用するには、「next/dynamic」からdynamic関数をインポートします。
import dynamic from 'next/dynamic';
ステップ2:コンポーネントを動的に読み込む
読み込みたいコンポーネントをdynamic関数でラップします。
const DynamicComponent = dynamic(() => import('./components/MyComponent'));
ステップ3:Loading状態を設定
ローディング状態を処理するコンポーネントを指定します。
const DynamicComponent = dynamic(
() => import('./components/MyComponent'),
{ loading: () => <p>Loading...</p> }
);
ステップ4:SSRオプションの設定
SSR環境での読み込み制御を設定します。
const DynamicComponent = dynamic(
() => import('./components/MyComponent'),
{ loading: () => <p>Loading...</p>, ssr: false }
);
実践的なコード例
例1:基本的な使用方法
// pages/index.js
import dynamic from 'next/dynamic';
const DynamicHeavyComponent = dynamic(
() => import('../components/HeavyComponent'),
{ loading: () => <div>読み込み中...</div> }
);
export default function Home() {
return (
<div>
<h1>ホームページ</h1>
<DynamicHeavyComponent />
</div>
);
}
例2:SSRを無効化する場合
ブラウザAPIに依存するコンポーネント(チャートライブラリなど)の場合、サーバーサイドレンダリングを無効にします。
// pages/dashboard.js
import dynamic from 'next/dynamic';
const DynamicChart = dynamic(
() => import('../components/Chart'),
{
loading: () => <div>チャート読み込み中...</div>,
ssr: false
}
);
export default function Dashboard() {
return (
<div>
<h1>ダッシュボード</h1>
<DynamicChart />
</div>
);
}
例3:複数コンポーネントの動的読み込み
// pages/products.js
import dynamic from 'next/dynamic';
const DynamicProductList = dynamic(
() => import('../components/ProductList')
);
const DynamicProductFilter = dynamic(
() => import('../components/ProductFilter'),
{ loading: () => <div>フィルター読み込み中...</div> }
);
export default function Products() {
return (
<div>
<h1>商品一覧</h1>
<DynamicProductFilter />
<DynamicProductList />
</div>
);
}
例4:TypeScriptでの実装
// pages/index.tsx
import dynamic from 'next/dynamic';
import type { ComponentType } from 'react';
interface MyComponentProps {
title: string;
content: string;
}
const DynamicMyComponent = dynamic<MyComponentProps>(
() => import('../components/MyComponent'),
{ loading: () => <div>Loading...</div> }
) as ComponentType<MyComponentProps>;
export default function Home() {
return (
<div>
<DynamicMyComponent
title=\"タイトル\"
content=\"コンテンツ\"
/>
</div>
);
}
例5:条件付き動的読み込み
// pages/conditional.js
import dynamic from 'next/dynamic';
import { useState } from 'react';
const DynamicAdminPanel = dynamic(
() => import('../components/AdminPanel'),
{ ssr: false }
);
export default function ConditionalLoading() {
const [isAdmin, setIsAdmin] = useState(false);
return (
<div>
<button onClick={() => setIsAdmin(!isAdmin)}>
{isAdmin ? '管理者を非表示' : '管理者を表示'}
</button>
{isAdmin && <DynamicAdminPanel />}
</div>
);
}
例6:エラーハンドリング付き
// pages/with-error-handling.js
import dynamic from 'next/dynamic';
const DynamicComponent = dynamic(
() => import('../components/MyComponent'),
{
loading: () => <div>Loading...</div>,
ssr: false
}
);
function ErrorFallback({ error }) {
return <div>エラーが発生しました: {error.message}</div>;
}
export default function Page() {
return (
<div>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<DynamicComponent />
</ErrorBoundary>
</div>
);
}
よくある間違いと対策
間違い1:デフォルトエクスポートでないコンポーネントの読み込み
dynamic importはデフォルトエクスポートを前提としています。名前付きエクスポートの場合はエラーが発生します。
// ❌ 間違い
const MyComponent = dynamic(() => import('./MyComponent'));
// ✅ 正解
const MyComponent = dynamic(() =>
import('./MyComponent').then(mod => mod.MyComponent)
);
間違い2:SSR=falseなしにブラウザAPIを使用
// ❌ 間違い:サーバーでwindowが存在しないためエラー
const MyComponent = dynamic(() => import('./MyComponent'));
// ✅ 正解
const MyComponent = dynamic(
() => import('./MyComponent'),
{ ssr: false }
);
間違い3:Loading状態のUIがない
ローディング状態を設定しないと、読み込み完了まで何も表示されず、ユーザー体験が悪くなります。
// ❌ 間違い
const MyComponent = dynamic(() => import('./MyComponent'));
// ✅ 正解
const MyComponent = dynamic(
() => import('./MyComponent'),
{ loading: () => <Skeleton /> }
);
間違い4:TypeScriptの型定義がない
// ❌ 間違い:型情報がない
const MyComponent = dynamic(() => import('./MyComponent'));
// ✅ 正解
import type { ComponentType } from 'react';
interface Props {
title: string;
}
const MyComponent = dynamic<Props>(
() => import('./MyComponent')
) as ComponentType<Props>;
間違い5:ループ内でのdynamic import
// ❌ 間違い:ループ内で動的にdynamic importを呼び出す
const components = [];
for (let i = 0; i < 10; i++) {
components.push(
dynamic(() => import(`./Component${i}`))
);
}
// ✅ 正解:外部でコンポーネントを定義
const Component0 = dynamic(() => import('./Component0'));
const Component1 = dynamic(() => import('./Component1'));
// ...
パフォーマンス最適化のコツ
1. 重いコンポーネントは優先的にdynamic import
バンドルサイズの大きなコンポーネント(チャートライブラリ、エディタなど)は、dynamic importで遅延読み込みすることで、初期ロード時間を短縮できます。
2. 複合的なLoading UIの実装
const LoadingSpinner = () => (
<div className=\"spinner\">\n <div className=\"spinner-ring\"></div>\n <p>読み込み中...</p>\n </div>\n);
const DynamicComponent = dynamic(
() => import('./HeavyComponent'),
{ loading: LoadingSpinner }
);
3. 条件付きSSRの活用
すべてのコンポーネントでSSRを無効化する必要はありません。ブラウザAPI使用時のみssr: falseに設定します。
まとめ
Next.jsのdynamic importは、アプリケーションのパフォーマンスを大幅に改善できる強力な機能です。本記事で紹介した主要ポイントは以下の通りです:
- 基本用法:next/dynamicをインポートしてコンポーネントをラップ
- SSRへの対応:ブラウザAPI使用時はssr: falseを設定
- UX向上:loading オプションでローディング状態を表示
- 型安全性:TypeScriptプロジェクトでは型定義を忘れずに
- エラー対策:デフォルトエクスポート、エラーハンドリングに注意
これらを正しく実装することで、高速で安定したNext.jsアプリケーションを構築できます。最初は基本的な使い方から始め、徐々に複雑なシナリオに対応していくことをお勧めします。

