Next.js dynamic importで発生するエラーの原因と解決方法を徹底解説
Next.jsでアプリケーションを開発していると、dynamic importを使用する際にエラーが発生することがあります。このエラーに直面すると、初心者は「何が原因なのか」「どうやって解決するのか」わからなくなってしまいます。本記事では、Next.js dynamic importで発生するエラーの原因から具体的な解決方法まで、わかりやすく説明します。
Next.js dynamic importとは
まず、Next.js dynamic importについて簡単に説明します。dynamic importは、モジュールを必要な時点で動的に読み込む機能です。これにより、初期読み込み時間を短縮し、アプリケーションのパフォーマンスを向上させることができます。
Next.jsではnext/dynamicを使用して、コンポーネントの遅延読み込みを実現します。特にLarge Bundle Sizeのコンポーネントや、条件付きで使用するコンポーネントに有効です。
dynamic importで発生する主なエラーの原因
1. SSRに対応していないコンポーネントの使用
Next.js dynamic importの最も一般的なエラーの原因は、SSR(Server-Side Rendering)に対応していないコンポーネントを無視する設定を忘れることです。window オブジェクトやlocalStorage、DOM操作など、ブラウザ環境でのみ動作するコードが含まれている場合、サーバー側で実行するとエラーが発生します。
2. コンポーネントのパスが正しくない
dynamic importで指定するコンポーネントのパスが間違っていると、モジュールが見つからずエラーが発生します。相対パスと絶対パスの混在や、ファイル拡張子の誤りも原因になります。
3. Named Exportとして定義されているコンポーネント
コンポーネントがnamed exportで定義されている場合、dynamic importで正しく読み込むには特別な設定が必要です。設定を忘れるとエラーが発生します。
4. 非同期インポートの処理漏れ
dynamic importは非同期処理です。その特性を理解せずに使用すると、コンポーネントが読み込まれる前にレンダリングが行われ、エラーが発生することがあります。
dynamic importエラーの解決手順
ステップ1: SSRを無効化する
ブラウザ専用の処理を含むコンポーネントを動的に読み込む場合は、SSRを無効化する必要があります。これが最も重要な解決方法です。
import dynamic from 'next/dynamic'
const MyComponent = dynamic(() => import('../components/MyComponent'), {
ssr: false
})
このコードで、MyComponentはクライアント側でのみ読み込まれるようになります。
ステップ2: コンポーネントのパスを確認する
コンポーネントのパスが正しいことを確認しましょう。相対パスの場合は、現在のファイルからの相対位置を正確に指定してください。
// 正しい例
const MyComponent = dynamic(() => import('../components/MyComponent'))
// 間違った例(拡張子を含めてはいけない)
// const MyComponent = dynamic(() => import('../components/MyComponent.jsx'))
ステップ3: ローディング状態を処理する
dynamic importは非同期処理のため、読み込み中の状態を処理することが重要です。loading オプションを使用してローディングコンポーネントを指定しましょう。
const MyComponent = dynamic(
() => import('../components/MyComponent'),
{ loading: () => <p>Loading...</p> }
)
ステップ4: エラーハンドリングを追加する
コンポーネントの読み込みに失敗した場合のエラーハンドリングも重要です。
const MyComponent = dynamic(
() => import('../components/MyComponent'),
{
loading: () => <p>Loading...</p>,
error: (error) => <p>Failed to load component</p>
}
)
具体的なコード例
例1: ブラウザ専用コンポーネントの動的読み込み
window オブジェクトを使用するコンポーネントの場合:
// components/ChartComponent.jsx
export default function ChartComponent() {
// ブラウザ環境でのみ動作するコード
const width = typeof window !== 'undefined' ? window.innerWidth : 0
return <div>Chart Width: {width}</div>
}
// pages/index.jsx
import dynamic from 'next/dynamic'
const ChartComponent = dynamic(
() => import('../components/ChartComponent'),
{ ssr: false, loading: () => <p>Loading chart...</p> }
)
export default function Home() {
return (
<div>
<h1>Welcome</h1>
<ChartComponent />
</div>
)
}
例2: Named Exportのコンポーネント
Named Exportで定義されているコンポーネントを動的に読み込む場合:
// components/SpecialComponent.jsx
export const SpecialComponent = () => {
return <div>Special Component</div>
}
// pages/index.jsx
import dynamic from 'next/dynamic'
const SpecialComponent = dynamic(
() => import('../components/SpecialComponent').then(mod => ({ default: mod.SpecialComponent })),
{ loading: () => <p>Loading...</p> }
)
export default function Home() {
return <SpecialComponent />
}
例3: 複数の動的インポート
複数のコンポーネントを同時に動的に読み込む場合:
import dynamic from 'next/dynamic'
const ComponentA = dynamic(() => import('../components/ComponentA'))
const ComponentB = dynamic(() => import('../components/ComponentB'))
const ComponentC = dynamic(() => import('../components/ComponentC'), { ssr: false })
export default function Home() {
return (
<div>
<ComponentA />
<ComponentB />
<ComponentC />
</div>
)
}
例4: 条件付き動的読み込み
ユーザーのアクション後にコンポーネントを読み込む場合:
import dynamic from 'next/dynamic'
import { useState } from 'react'
const HeavyComponent = dynamic(
() => import('../components/HeavyComponent'),
{ loading: () => <p>Loading heavy component...</p> }
)
export default function Home() {
const [showComponent, setShowComponent] = useState(false)
return (
<div>
<button onClick={() => setShowComponent(true)}>
Load Component
</button>
{showComponent && <HeavyComponent />}
</div>
)
}
よくある間違いと対策
間違い1: ssr: false を忘れる
ブラウザ環境でのみ動作するコンポーネントなのに、ssr: false オプションを指定しないと、サーバー側でもレンダリングが試みられてエラーが発生します。
// ❌ 間違った例
const MyComponent = dynamic(() => import('../components/MyComponent'))
// ✅ 正しい例
const MyComponent = dynamic(() => import('../components/MyComponent'), { ssr: false })
間違い2: ファイル拡張子を含める
dynamic importでファイル拡張子を含めると、モジュール解決に失敗します。
// ❌ 間違った例
const MyComponent = dynamic(() => import('../components/MyComponent.jsx'))
// ✅ 正しい例
const MyComponent = dynamic(() => import('../components/MyComponent'))
間違い3: loading オプションの値が不正
loading オプションには、Reactコンポーネントを返す関数を指定する必要があります。文字列などを指定するとエラーが発生します。
// ❌ 間違った例
const MyComponent = dynamic(
() => import('../components/MyComponent'),
{ loading: 'Loading...' }
)
// ✅ 正しい例
const MyComponent = dynamic(
() => import('../components/MyComponent'),
{ loading: () => <p>Loading...</p> }
)
間違い4: dynamic 関数の外で非同期処理を使用する
dynamic の第一引数には、import 文を含む関数をそのまま渡す必要があります。async関数を使用するとエラーが発生します。
// ❌ 間違った例
const MyComponent = dynamic(async () => {
const mod = await import('../components/MyComponent')
return mod
})
// ✅ 正しい例
const MyComponent = dynamic(() => import('../components/MyComponent'))
間違い5: Dynamic Routing でのパス指定ミス
Dynamic Routingを使用する場合、ファイルパスの指定が特に重要です。以下のような間違いが多く見られます。
// ❌ 間違った例:ファイルシステムのパスを使用
const MyComponent = dynamic(() =>
import('/absolute/path/to/components/MyComponent')
)
// ✅ 正しい例:相対パスまたはモジュール名を使用
const MyComponent = dynamic(() =>
import('../components/MyComponent')
)
トラブルシューティング
「Module not found」エラーが出る場合
コンポーネントへのパスを確認してください。相対パスで指定する場合は、現在のファイルからの位置を正確に計算する必要があります。
// ファイル構造
// pages/index.jsx
// components/MyComponent.jsx
// pages/index.jsx から components/MyComponent.jsx へのパス
const MyComponent = dynamic(() => import('../components/MyComponent'))
「window is not defined」エラーが出る場合
これはSSR無効化の忘れです。ssr: false を追加してください。
const MyComponent = dynamic(
() => import('../components/MyComponent'),
{ ssr: false }
)
コンポーネントが表示されない場合
loading オプションが正しく機能しているか確認してください。ネットワークタブでコンポーネントが読み込まれているか確認しましょう。
パフォーマンス最適化のコツ
1. 優先度に応じた読み込み
すべてのコンポーネントをdynamic importする必要はありません。初期表示に必要なコンポーネントは通常のimportで読み込み、補助的なコンポーネントだけdynamic importにしましょう。
2. プリロードの活用
ユーザーがクリックする可能性が高いコンポーネントは、マウスホバー時にプリロードすると、ユーザー体験が向上します。
import { useState } from 'react'
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('../components/HeavyComponent'))
export default function Home() {
const handleMouseEnter = () => {
// プリロード処理
import('../components/HeavyComponent')
}
return (
<button onMouseEnter={handleMouseEnter}>
Hover to preload
</button>
)
}
3. バンドルサイズの確認
next/analyze を使用してバンドルサイズを確認し、dynamic import が効果的に機能しているか確認しましょう。
まとめ
Next.js dynamic importは、アプリケーションのパフォーマンス向上に非常に効果的な機能です。しかし、正しい使い方を理解していないとエラーが発生してしまいます。
本記事で紹介した主要なポイントは以下の通りです:
- SSRに対応していないコンポーネントには
ssr: falseを指定する - コンポーネントのパスを正確に指定する(拡張子は含めない)
- loading とerror オプションでユーザー体験を向上させる
- ファイル拡張子、async/await の不正使用を避ける
- 「Module not found」「window is not defined」などのよくあるエラーに対応できる
これらのポイントを押さえることで、Next.js dynamic importを正しく活用でき、より高速で効率的なアプリケーションを構築できます。初心者の方でも、本記事のコード例に従えば、dynamic importの問題を解決できるはずです。
最後に、Next.js の公式ドキュメントも定期的に確認することをお勧めします。バージョンアップに伴う仕様変更もあるため、常に最新の情報を得ることが重要です。

