☺️

Next.js 16 + Turbopack環境でバンドル分析できるようになるよ

に公開

はじめに

Next.js 16では、next devnext buildの両方でTurbopackがデフォルトになり、開発サーバーの起動やホットリロードが大幅に高速化されました。しかし、この変化により従来のバンドル分析ツールとの互換性に課題が生じています。

本記事では、Next.js 16 + Turbopack環境で、バンドルサイズを効果的に分析する方法を紹介します。特に、Next.js canaryバージョンに含まれるexperimental-analyzeコマンドの活用方法に焦点を当てます。

このコマンドは、Next.jsチームのTim Neutkens氏が2025年11月19日にXで発表した新しいバンドルアナライザーで、TurbopackのモジュールグラフとNext.jsのルート構造を完全に認識したツールです。ツイートでは、「モジュールのロード場所を推測する必要がなくなり、フルインポートスタックを表示してバンドルに含まれるすべてのパスを確認できる」と説明されており、CSSや他のアセット、サーバーバンドルも検査可能で、将来的にgzip/brotli圧縮サイズの表示も予定されているとのことです。 オープンソースアプリケーションでの実行例も共有されており、開発チームにとってバンドルサイズのデバッグが大幅に効率化される見込みです。


 
注意: 本記事の例では、このプロジェクトで使用しているBunパッケージマネージャーを使用していますが、experimental-analyzeコマンド自体はnpm、yarn、pnpmなど、どのパッケージマネージャーでも使用できます。

背景:なぜバンドル分析が重要か

Webアプリケーションのパフォーマンスにおいて、バンドルサイズは重要な指標です。大きなバンドルサイズは以下の問題を引き起こします:

  • 初回読み込み時間の増加: ユーザーが最初にページを開くまでの時間が長くなる
  • モバイル環境でのパフォーマンス低下: 限られた帯域幅での読み込みが遅くなる
  • SEOへの影響: Core Web Vitalsのスコアに影響を与える可能性があります
  • 開発効率の低下: デバッグやビルド時間が長くなる

特に、Next.js 16でnext devnext buildの両方でTurbopackがデフォルトになったことで、開発時のビルドプロセスが変化し、従来のWebpackベースのバンドル分析ツールとの互換性に課題が生じました。

課題:Next.js 16 + Turbopack環境でのバンドル分析

従来、Next.jsアプリケーションのバンドル分析には@next/bundle-analyzerが広く使用されてきました。しかし、Next.js 16でTurbopackがデフォルトで有効になると、以下の警告が表示されます:

$ ANALYZE=true next build
The Next Bundle Analyzer is not compatible with Turbopack builds yet, no report will be generated.

To run this analysis pass the `--webpack` flag to `next build`

この警告は、Turbopackビルドでは@next/bundle-analyzerが動作しないことを示しています。--webpackフラグを使用すれば動作しますが、これはフォールバックです。Next.js 16ではWebpackはlegacy fallbackとして扱われており、今後はTurbopack側が第一級でメンテされていくと考えられます。将来的にWebpackが縮小・廃止される可能性も高いです。また、Turbopackを使用したビルドを分析できないという制約があります。

解決策:experimental-analyzeコマンド

Next.jsのcanaryバージョンでは、experimental-analyzeコマンドが利用可能です(本記事執筆時点では16.1.0-canary.1で動作確認済み)。
このコマンドは、Tim Neutkens氏のツイートで紹介されたように、Next.jsルートとTurbopackモジュールグラフを意識したバンドルアナライザーで、モジュールのインポートスタックをすべて表示し、バンドルへの包含パスを簡単に追跡できます。また、CSSサイズや他のアセット、サーバーバンドルも分析可能で、出力サイズは現在圧縮なしですが、将来的にgzip/brotli対応が予定されています。

注意: experimental-analyzeは実験的機能ですが、Next.jsチームは今後安定版に統合する予定です。Turbopackがプロダクションビルドも置き換える段階で、正式なanalyzerとして提供される見込みです。

コマンドの仕様

experimental-analyze --helpの出力によると、このコマンドの仕様は以下の通りです:

Usage: next experimental-analyze [options] [directory]

Analyze bundle output. Does not produce build artifacts. Only compatible with
Turbopack.

Arguments:
  [directory]    A directory on which to analyze the application. If no
                 directory is provided, the current directory will be used.

Options:
  --no-mangling  Disables mangling.
  --profile      Enables production profiling for React.
  --serve        Serve the bundle analyzer in a browser after analysis.
  --port <port>  Specify a port number to serve the analyzer on. (default:
                 4000, env: PORT)
  -h, --help     Displays this message.

主な特徴

  1. Turbopack専用: Turbopackを使用したビルドを分析対象とします
  2. ビルド成果物を生成しない: プロダクション相当のTurbopackビルドを「分析モード」で実行し、本番用のclient/server bundleや静的ファイルは出力しません。代わりに、.next/diagnostics/analyze配下にTreemap UIと分析結果(JSONなど)の分析用ファイルのみが生成されます
  3. ブラウザで表示可能: --serveオプションで分析結果をブラウザで確認できます

重要な理解: experimental-analyzeは開発サーバー(next dev)の内部状態を分析するものではなく、Turbopackを使用した本番相当のビルドプロセスを分析用に実行するコマンドです。実体は「Turbopackを使ったフルビルド(ただし出力しない)」です。

セットアップ

1. Next.jsをcanaryバージョンにアップグレード

cd services/your-service

# Bunを使用する場合(このプロジェクトの例)
bun add next@canary eslint-config-next@canary

# npmを使用する場合
npm install next@canary eslint-config-next@canary

# yarnを使用する場合
yarn add next@canary eslint-config-next@canary

# pnpmを使用する場合
pnpm add next@canary eslint-config-next@canary

2. バージョン確認

# Bunの場合(このプロジェクトの例)
bunx next --version
# npmの場合: npx next --version
# yarnの場合: yarn next --version
# pnpmの場合: pnpm next --version
# canaryバージョンであることを確認(例: 16.1.0-canary.1)

3. package.jsonにスクリプトを追加(推奨)

各サービスのpackage.jsonanalyzeスクリプトを追加すると便利です:

{
  "scripts": {
    "analyze": "next experimental-analyze --serve"
  }
}

基本的な使用方法

基本的な実行

現在のディレクトリを分析する場合:

# Bunを使用する場合(このプロジェクトの例)
bunx next experimental-analyze --serve

# npmを使用する場合
npx next experimental-analyze --serve

# yarnを使用する場合
yarn next experimental-analyze --serve

# pnpmを使用する場合
pnpm next experimental-analyze --serve

# package.jsonにスクリプトを追加している場合
bun run analyze  # または npm run analyze, yarn analyze, pnpm analyze

デフォルトでポート4000でサーバーが起動します。ブラウザで http://localhost:4000 にアクセスしてバンドル分析結果を確認できます。

オプションの使い方

ディレクトリを指定する場合:

bunx next experimental-analyze --serve services/okamai-client-console

カスタムポートを指定する場合:

# ポート番号を指定
bunx next experimental-analyze --serve --port 4001

# 環境変数で指定
PORT=4001 bunx next experimental-analyze --serve

マングリングを無効化する場合(デバッグ時に有用):

bunx next experimental-analyze --serve --no-mangling

Reactのプロダクションプロファイリングを有効化する場合:

bunx next experimental-analyze --serve --profile

複数のオプションを組み合わせる場合:

bunx next experimental-analyze --serve --no-mangling --profile --port 4001

バンドルアナライザーUIの使い方

experimental-analyze --serveコマンドを実行すると、ブラウザでNext.js Bundle AnalyzerのUIが表示されます。
このUIはNext.jsルートを意識した設計で、モジュールのフルインポートスタックを表示し、バンドルへの複数のパスを追跡可能。CSSやアセット、サーバーバンドルの検査もサポートしています。

バンドルアナライザーの初期画面

図1: バンドルアナライザーの初期画面。デフォルトでは/agents/[id]ルートが選択され、Clientバンドルが表示されています。

UIの構成要素

1. ルート選択

画面左上のcomboboxで、分析対象のルート(ページ)を選択できます。

ルートの種類:

  • ページルート: /dashboard, /agents, /chats など、ユーザーがアクセスするページ
  • APIルート: /api/auth/[...better-auth], /api/client/agents など、APIエンドポイント
  • 動的ルート: /agents/[id], /chats/[chatId] など、動的パラメータを含むルート
  • 特殊ルート: /_not-found, /favicon.ico など、Next.jsの特殊なルート

操作方法:

  1. comboboxをクリックすると、ドロップダウンが開きます
  2. ドロップダウン内でルートを検索できます(キーボード入力で絞り込み可能)
  3. ルートをクリックすると選択され、そのルートのバンドル情報が読み込まれます
  4. 読み込みには数秒かかる場合があります

注意点:

  • ルートを選択すると、Treemapが再描画されます
  • 選択したルートのバンドル情報が存在しない場合、エラーが表示されることがあります

ルート選択のドロップダウン

図2: ルート選択のドロップダウンが開いた状態。プロジェクト内のすべてのルートが一覧表示されます。

ルート選択後の画面

2. Client/Server切り替え

Client(クライアントサイド):

  • デフォルト状態: 選択されています
  • 表示内容: ブラウザで実行されるJavaScriptバンドル
  • 用途: 初回読み込み時間や、クライアントサイドのパフォーマンス分析に使用
  • 特徴: Reactコンポーネント、クライアントサイドのロジック、ブラウザAPIを使用するコードが含まれます

Server(サーバーサイド):

  • デフォルト状態: 選択されていません
  • 表示内容: サーバーで実行されるバンドル
  • 用途: サーバーサイドのレンダリングやAPIルートの分析に使用
  • 特徴: サーバーコンポーネント、サーバーサイドのロジック、データベースアクセスなどが含まれます

切り替え方法:

  1. ClientまたはServerのラジオボタンをクリック
  2. 選択した方のバンドル情報が表示されます
  3. Treemapが再描画され、選択したバンドルの内容が表示されます

使い分け:

  • Client: ユーザーが最初にダウンロードするバンドルサイズを確認したい場合
  • Server: サーバーサイドのレンダリングやAPIのパフォーマンスを確認したい場合

Next.jsのApp Routerでは、各ルートがクライアントとサーバーの両方のバンドルを持つため、それぞれを個別に分析できます。

重要な注意: Server bundleはユーザーに送られないため、ネットワーク転送サイズやクライアント側の実行時間に直接効くのは主にClient bundleです。一方で、Server bundleが重いとSSRの処理時間が伸びてTTFBが悪化し、間接的にLCPなどのCore Web Vitalsにも影響し得ます。そのため、Server bundleは「SSRの応答速度・メモリ使用量」を主な評価軸としつつも、TTFB→LCPへの影響もあわせて見るとよいです。

Serverバンドルの表示

図3: Serverラジオボタンを選択した状態。サーバーサイドのバンドルが表示されます。ClientとServerでバンドルサイズが異なる場合があります。

3. ファイルタイプフィルター

Client/Server切り替えの下に、ファイルタイプのフィルターボタンがあります。

各ボタンの詳細:

  • JS(JavaScript):

    • デフォルト状態: 有効
    • 表示内容: .js, .jsx, .ts, .tsxなどのJavaScriptファイル
    • 用途: JavaScriptバンドルのサイズを確認
    • 特徴: 最も重要なファイルタイプで、通常は最大のサイズを占めます
  • CSS(スタイルシート):

    • デフォルト状態: 有効
    • 表示内容: .cssファイルやCSS-in-JSのバンドル
    • 用途: スタイルシートのサイズを確認
    • 特徴: Tailwind CSSなどのユーティリティCSSフレームワークを使用している場合、CSSバンドルのサイズが大きくなる傾向があります
  • JSON(データファイル):

    • デフォルト状態: 有効
    • 表示内容: .jsonファイル
    • 用途: JSONデータのサイズを確認
    • 特徴: 設定ファイルやデータファイルが含まれます。ただし、TurbopackではJSONはloaderでJSモジュールへ変換される構成もあり、Treemap上ではJS側に畳み込まれて表示される場合があります
  • Asset(その他のアセット):

    • デフォルト状態: 無効
    • 表示内容: 画像、フォント、その他のアセットファイル
    • 用途: アセットファイルのサイズを確認
    • 特徴: 画像やフォントファイルなどが含まれます

操作方法:

  1. 各ボタンをクリックすると、そのファイルタイプの表示/非表示が切り替わります
  2. ボタンが押されている状態(pressed状態)のファイルタイプがTreemapに表示されます
  3. 複数のファイルタイプを同時に選択可能です(例: JSとCSSを同時に表示)
  4. すべてのボタンを無効にすると、Treemapが空になります

使用例:

  • JSのみ表示: JSボタンのみを有効にして、JavaScriptバンドルのみを分析
  • CSSのみ表示: CSSボタンのみを有効にして、スタイルシートのサイズを確認
  • すべて表示: すべてのボタンを有効にして、全体のバンドルサイズを確認

Assetフィルターを有効にした状態

図4: Assetボタンをクリックして有効にした状態。AssetファイルタイプがTreemapに表示されます。複数のファイルタイプを同時に選択可能です。

4. ファイル検索

ファイルタイプフィルターの下に、"Search files..."というプレースホルダーを持つ検索ボックスがあります。

機能:

  • ファイル名やパスを入力して、該当するファイルを検索できます
  • 検索はリアルタイムで実行され、入力と同時に結果が更新されます

使用方法:

  1. 検索ボックスをクリックしてフォーカスを当てます
  2. ファイル名やパスを入力します(例: react, node_modules, components
  3. 入力に一致するファイルがTreemap上でハイライト表示されます
  4. 検索結果をクリアするには、検索ボックスの内容を削除します

使用例:

  • 特定のパッケージを検索: reactと入力して、React関連のファイルを検索
  • 特定のディレクトリを検索: componentsと入力して、コンポーネントディレクトリ内のファイルを検索
  • node_modulesを検索: node_modulesと入力して、依存関係のファイルを検索

注意点:

  • 検索はTreemapに表示されているファイルに対して行われます
  • ファイルタイプフィルターで非表示にしたファイルタイプは検索結果に含まれません

検索機能の使用例

図5: 検索ボックスにreactと入力した状態。React関連のファイルがTreemap上でハイライト表示されます。

5. Treemap(メイン表示エリア)

画面中央の大きなエリアには、バンドルサイズを視覚化したTreemap(SVG形式)が表示されます。

Treemapの仕組み:

  • 矩形のサイズ: 各矩形のサイズがファイルのバンドルサイズを表します。大きい矩形ほど大きなファイルであることを示します
  • 矩形の配置: 関連するファイルが近くに配置され、階層構造が視覚的に表現されます
  • 色の意味: 矩形の色でファイルの種類やカテゴリを区別できます

技術的な補足: Treemapに表示されるのは、Turbopackが解析したdependency graph + chunking結果です。これはWebpackのbundle-analyzerとは少し異なる点ですが、UI上は「bundle size」として表示されるため、実用上の違いはほとんどありません。

操作方法:

  1. マウスオーバー(ホバー):

    • ファイルにマウスを合わせると、そのファイルの詳細情報がツールチップで表示されます
    • ツールチップには、ファイル名、パス、サイズなどの情報が含まれます
    • 画面下部に「Hover over a file to see details」というヒントが表示されています
  2. クリック:

    • ファイルをクリックすると、サイドバーに詳細情報が表示されます
    • 選択されたファイルのパスが「Selected Source」に表示されます
    • ファイルのサイズが「Output Size」に表示されます
  3. 視覚的な分析:

    • 矩形の大きさで、どのファイルがバンドルサイズに大きく寄与しているかを直感的に把握できます
    • 大きな矩形を特定することで、最適化の優先順位を決定できます
    • 複数のファイルを比較して、サイズの違いを視覚的に確認できます

Treemapの特徴:

  • SVG形式で実装されているため、拡大縮小が可能です
  • インタラクティブに操作でき、ファイルの詳細情報を簡単に確認できます
  • ファイルタイプフィルターや検索機能と連動して動作します

Treemapは、バンドルサイズを視覚的に理解するための強力なツールです。矩形の大きさで、どのファイルがバンドルサイズに大きく寄与しているかを直感的に把握できます。

6. サイドバー

画面右側には、選択したファイルの詳細情報を表示するサイドバーがあります。

サイドバー(ファイル未選択時)

図6: サイドバーの初期状態。ファイルが選択されていない場合、「Output Size」は「0 B」と表示されます。

表示内容:

  1. Selected Source: 選択したファイルのパス
  2. Output Size: ファイルの出力サイズ(バイト単位)
  3. Output Chunks: 選択したファイルが含まれる出力チャンク(バンドルファイル)の一覧
  4. Import chain: 選択したファイルの依存関係

サイドバー(ファイル選択時)

図7: ファイルを選択した後のサイドバー。選択したファイルのパス、サイズ、依存関係が確認できます。

Output Chunksについて:

Output Chunksには、選択したファイルが実際にどの出力チャンク(バンドルファイル)に含まれているかが表示されます。

用途:

  • コード分割の確認: ファイルが複数のチャンクに含まれている場合、コード分割が適切に機能しているかを確認できます
  • チャンクサイズの評価: 各チャンクのサイズを確認し、大きなチャンクを特定できます
  • 共通チャンクの特定: 複数のルートで使用されるファイルが共通チャンクに適切に抽出されているかを確認できます

表示例:

  • _app-[hash].js: アプリケーション全体で共有されるチャンク
  • [route]-[hash].js: 特定のルート専用のチャンク
  • chunks/[hash].js: 共通チャンク

分析のポイント:

  • ファイルが複数のチャンクに含まれている場合、そのファイルが複数のルートで使用されている可能性があります
  • 大きなチャンクに含まれているファイルは、そのチャンクのサイズに大きく寄与している可能性があります
  • 共通チャンクに適切に抽出されているファイルは、複数のルートで効率的に共有されています

Import chainの読み方:

Import chainには、選択したファイルが依存している様々なモジュールやファイルが表示されます。

パスの種類と意味:

  • node_modules/で始まるパス: 外部パッケージ。大きなパッケージはバンドルサイズに大きく寄与する可能性があります
  • next/で始まるパス: Next.jsフレームワークのモジュール
  • dist/build/などのディレクトリ: ビルド済みの配布用ファイル
  • esm/es/などのディレクトリ: ES Modules形式。Tree Shakingが効きやすい形式です
  • cjs/lib/などのディレクトリ: CommonJS形式。Tree Shakingが効きにくい場合があります
  • プロジェクト内のパス: プロジェクト内のソースファイルや相対パス、パスエイリアス

「Show all dependents (including those outside current route)」チェックボックス:

Import chainセクションの下部には、「Show all dependents (including those outside current route)」というチェックボックスがあります。

デフォルト状態: 無効(チェックされていない)

機能:

  • チェックなし(デフォルト): 現在選択しているルート内でのみ使用されている依存関係のみを表示します
  • チェックあり: 現在のルート外の依存関係も含めて、すべての依存関係を表示します

使用例:

  • ルート固有の依存関係を確認: チェックなしの状態で、そのルートで使用されている依存関係のみを確認できます
  • 全体の依存関係を確認: チェックありの状態で、他のルートでも使用されている共通の依存関係を確認できます
  • コード分割の評価: 複数のルートで使用されている依存関係が共通チャンクに適切に抽出されているかを確認できます

分析の勘所:

  1. 大きな依存関係を特定: Import chainで大きな依存関係を特定し、それが本当に必要かどうかを検討します
  2. モジュール形式を確認: esmcjsの違いを確認し、Tree Shakingが効きやすい形式(ES Modules)を使用しているか確認します
  3. 循環依存を確認: プロジェクト内のファイル間で循環依存がないか確認します
  4. 不要な依存関係を特定: 使用していない機能をインポートしていないか確認します
  5. 「Show all dependents」を活用: チェックボックスを有効にして、現在のルート外の依存関係も含めて確認します。これにより、複数のルートで使用されている共通の依存関係を特定できます
  6. Output Chunksと組み合わせる: Output Chunksでファイルが含まれるチャンクを確認し、Import chainで依存関係を確認することで、コード分割が適切に機能しているかを総合的に評価できます

リサイズ機能:

  • サイドバーとメイン表示エリアの間に「Resize sidebar」というボタンがあります
  • このボタンをドラッグすることで、サイドバーの幅を調整できます
  • サイドバーの幅を調整することで、Treemapの表示領域を変更できます

使用方法:

  1. Treemap上のファイルをクリックします
  2. サイドバーにファイルの詳細情報が表示されます(図7を参照)
  3. 「Selected Source」でファイルのパスを確認できます
  4. 「Output Size」でファイルのサイズを確認できます
  5. 「Output Chunks」でファイルが含まれるチャンクを確認できます
  6. 「Import chain」で依存関係を確認できます
  7. 「Show all dependents」チェックボックスを切り替えて、表示範囲を調整できます
  8. 必要に応じて、リサイズボタンでサイドバーの幅を調整します

注意点:

  • ファイルが選択されていない場合、サイドバーにはデフォルトの情報のみが表示されます(図6を参照)
  • 「Output Size」が「0 B」と表示される場合: これは、Treemap上のファイルが正しく選択されていないことを示します。Treemap上の矩形を直接クリックする必要があります
  • 「Output Chunks」と「Import chain」は、ファイルを選択した場合のみ表示されます
  • 「Show all dependents」チェックボックスを有効にすると、現在のルート外の依存関係も含めて表示されます。これにより、表示される依存関係の数が増える場合があります

ファイルを正しく選択する方法:

  1. Treemapが完全に読み込まれるまで待ちます(数秒かかる場合があります)
  2. 複数選択を避ける: 既に選択されているファイルがある場合、Treemapの空白部分をクリックして選択をクリアします
  3. Treemap上の矩形(ファイルを表す四角形)にマウスを合わせると、ツールチップが表示されます
  4. 単一のファイルを選択: ツールチップが表示された状態で、1つの矩形のみをクリックします。複数の矩形が選択されると、Import chainが正しく表示されない場合があります
  5. サイドバーにファイルのパスとサイズが正しく表示されることを確認します
  6. 「Output Size」が「0 B」のままの場合は、別の矩形をクリックして再試行します

複数選択を避けるコツ:

  • Treemap上の1つの矩形のみをクリックします
  • 複数の矩形が同時に選択されている場合、Treemapの空白部分をクリックして選択をクリアしてから、再度1つの矩形をクリックします
  • 矩形の中央部分をクリックすることで、単一選択が確実になります

サイドバーの詳細表示(Import chain表示)

図8: サイドバーの詳細表示。「Output Chunks」セクションと「Import chain」セクションが表示され、「Show all dependents (including those outside current route)」チェックボックスが確認できます。この例では依存関係がないため「No dependents found」と表示されています。

バンドルサイズの目安と分析の観点

バンドルアナライザーを使用する際は、単にサイズを確認するだけでなく、パフォーマンスへの影響を多角的に評価することが重要です。

Output Sizeの目安

参考根拠: 以下の目安は、一般的なウェブパフォーマンスのベストプラクティスと、Googleの調査データに基づいています。

  • ページの読み込み時間が1秒増加するごとに、訪問者の離脱率が10%増加するというデータがあります (wp.techtarget.itmedia.co.jp)
  • モバイルサイトの読み込み時間が1秒から3秒に増加すると、直帰率が32%増加することが報告されています (imgix.com)

初回読み込み(Initial Load)の目安:

一般的なウェブページ全体のサイズの目安として、以下の基準が参考になります (cloudflare.com):

  • 優秀: 1MB未満(圧縮前)
  • 良好: 1MB〜2MB(圧縮前)
  • 要改善: 2MB〜3MB(圧縮前)
  • 問題: 3MB以上(圧縮前)

注意:

  • バンドルアナライザーに表示されるサイズは圧縮前のサイズです。実際のネットワーク転送時はgzip圧縮が適用されるため、通常は約30-40%のサイズになります。JavaScriptファイルの場合、圧縮率は約30-40%が一般的です。
  • 上記の目安はページ全体のサイズを基準としています。JavaScriptバンドルのみの場合は、これより小さな値を目標とします。
  • 実際のプロジェクトでは、GoogleのPageSpeed InsightsやLighthouseなどのツールで測定した結果を基準にすることが推奨されます。

JavaScriptバンドルサイズの参考値:

JavaScriptバンドルサイズについては、明確な公式推奨値はありませんが、一般的なベストプラクティスとして以下の参考値があります。Googleの調査によれば、ページの75%で2.5秒以内に収まるようにすることが推奨されています (Google News Initiative)。この目標を達成するための一般的な目安として、以下の参考値があります:

  • 初回読み込み(gzip圧縮後): 200KB以下を目標とします(参考値)
  • 個別ファイル(圧縮前):
    • 小さなファイル: 10KB未満 - 通常は問題なし(参考値)
    • 中程度のファイル: 10KB〜50KB - 内容を確認し、最適化の余地を検討(参考値)
    • 大きなファイル: 50KB〜100KB - 最適化を検討すべき(参考値)
    • 非常に大きなファイル: 100KB以上 - 優先的に最適化が必要(参考値)

ルートごとの総バンドルサイズの参考値:

以下の値は、一般的なベストプラクティスに基づく参考値です:

  • 軽量なルート: 200KB未満(圧縮前)
  • 標準的なルート: 200KB〜500KB(圧縮前)
  • 重いルート: 500KB〜1MB(圧縮前)
  • 非常に重いルート: 1MB以上(圧縮前) - 緊急に最適化が必要

重要: これらの数値は参考値であり、プロジェクトの性質や要件によって適切なサイズは異なります。実際の最適化を行う際は、以下の点を考慮してください:

  1. パフォーマンス測定ツールの活用: Lighthouse、PageSpeed Insights、WebPageTestなどのツールで実際の読み込み時間を測定します
  2. Core Web Vitalsの確認: LCP、INP、CLSなどの指標を確認し、バンドルサイズがこれらの指標に与える影響を評価します
  3. ユーザー環境の考慮: ターゲットユーザーのネットワーク環境(4G、3G、低速接続など)を考慮します
  4. 継続的なモニタリング: 最適化後も定期的にバンドルサイズを監視し、増加を防ぎます

分析の観点

1. 初回読み込み時間への影響

分析のポイント:

  • Clientバンドルの総サイズを確認します
  • 大きなファイルが初回読み込みに含まれているかを確認します
  • 動的インポートで分割できるファイルがないかを検討します

最適化の優先順位:

  1. 初回読み込みに含まれる100KB以上のファイル
  2. 使用頻度の低い機能を含む大きなファイル
  3. 複数のルートで共通して使用される大きな依存関係

2. 総バンドルサイズの評価

分析のポイント:

  • ルートごとの総バンドルサイズを比較します
  • ClientとServerのバンドルサイズを比較します(ただし、評価基準は異なることに注意)
  • ファイルタイプごとのサイズを確認します

注意: Server bundleはユーザーに送られないため、ネットワーク転送サイズやクライアント側の実行時間に直接効くのは主にClient bundleです。一方で、Server bundleが重いとSSRの処理時間が伸びてTTFBが悪化し、間接的にLCPなどのCore Web Vitalsにも影響し得ます。そのため、Server bundleは「SSRの応答速度・メモリ使用量」を主な評価軸としつつも、TTFB→LCPへの影響もあわせて見るとよいです。

評価基準(参考値):

  • JSバンドル: 通常は最大のサイズを占めます。初回読み込み時のJavaScriptバンドルは、200KB以下(gzip圧縮後)を目標とします。これは、一般的なベストプラクティスに基づく参考値です
  • CSSバンドル: 50KB以下を目標とします。Tailwind CSSを使用している場合、未使用のクラスを削除することで大幅に削減できます
  • JSONバンドル: 設定ファイルやデータファイル。20KB以下を目標とします
  • Assetバンドル: 画像やフォント。必要に応じて最適化します

3. チャンクサイズの評価

分析のポイント:

  • 各チャンク(ファイル)のサイズを確認します
  • 大きなチャンクが複数ある場合、分割の余地を検討します
  • 小さなチャンクが多数ある場合、統合の余地を検討します

最適化の目安(参考値):
以下の値は、HTTPリクエストのオーバーヘッドとファイルサイズのバランスを考慮した一般的な目安です:

  • 理想的なチャンクサイズ: 20KB〜50KB(圧縮前)(参考値)
  • 小さすぎるチャンク: 5KB未満のチャンクが多数ある場合、HTTPリクエストのオーバーヘッドが問題になる可能性があります(参考値)
  • 大きすぎるチャンク: 100KB以上のチャンクは分割を検討します(参考値)

4. 依存関係の評価

分析のポイント:

  • Import chainで依存関係の深さを確認します
  • 大きな依存関係が本当に必要かを検討します
  • 同じパッケージの異なるバージョンが使用されていないかを確認します

評価基準(参考値):
以下の値は、一般的なベストプラクティスに基づく参考値です:

  • 依存関係の深さ: 3階層以下を目標とします(参考値)。深すぎる依存関係は、バンドルサイズの増加や予期しない動作の原因になる可能性があります
  • 外部パッケージのサイズ: node_modules内の大きなパッケージ(100KB以上)は、代替パッケージや必要な部分だけのインポートを検討します(参考値)
  • モジュール形式: ES Modules形式のパッケージを優先します。CommonJS形式のパッケージは、Tree Shakingが効きにくい場合があります

5. コード分割の評価

分析のポイント:

  • ルートごとのバンドルサイズを比較します
  • 共通チャンクが適切に抽出されているかを確認します
  • 動的インポートが適切に使用されているかを確認します

最適化の目安(参考値):
以下の値は、一般的なベストプラクティスに基づく参考値です:

  • 共通チャンク: 複数のルートで使用される依存関係は、共通チャンクに抽出します。共通チャンクのサイズは、100KB〜200KBが適切です(参考値)
  • ルート固有のチャンク: 各ルート固有のコードは、50KB以下を目標とします(参考値)
  • 動的インポート: 使用頻度の低い機能(モーダル、ドロップダウンなど)は動的インポートを使用します

6. パフォーマンス指標との関係

Core Web Vitalsとの関係:

GoogleのCore Web Vitalsは、ウェブページのユーザーエクスペリエンスを評価する指標です。バンドルサイズは、これらの指標に直接影響を与えます (it-bell.com):

  • LCP (Largest Contentful Paint): ページの主要なコンテンツが読み込まれるまでの時間。初回読み込みのバンドルサイズが大きいと、LCPが遅延します。目標は2.5秒以内です (Google Core Web Vitals)
  • INP (Interaction to Next Paint): ユーザーの操作に対する応答性。JavaScriptバンドルが大きいと、INPが遅延します。目標は200ミリ秒以内です (Google Core Web Vitals)
  • CLS (Cumulative Layout Shift): 視覚的な安定性。CSSバンドルが大きいと、レイアウトシフトが発生する可能性があります。目標は0.1以下です (Google Core Web Vitals)

参考: これらの指標の目標値は、GoogleのCore Web Vitalsの公式ドキュメントに基づいています。バンドルサイズの具体的な数値については、プロジェクトの性質や要件によって異なるため、実際のパフォーマンス測定ツールで確認することが重要です。

ネットワーク環境を考慮した評価(参考値):

ネットワーク環境によって、適切なバンドルサイズは異なります。以下の値は一般的な参考値です:

  • 4G接続: 200KB以下を目標とします(参考値)
  • 3G接続: 100KB以下を目標とします(参考値)
  • 低速接続: 50KB以下を目標とします(参考値)

モバイル環境での考慮事項:

  • モバイルデバイスでは、デスクトップよりも処理能力が低いため、バンドルサイズの影響が大きくなります
  • モバイル環境では、100KB以下のバンドルサイズを目標とします(参考値)

分析の優先順位

最適化を行う際は、以下の優先順位で分析と改善を行います:

  1. 緊急度が高い:

    • 初回読み込みに含まれる500KB以上のファイル
    • 総バンドルサイズが1MB以上のルート
    • 100KB以上の外部パッケージ
  2. 優先度が高い:

    • 初回読み込みに含まれる200KB〜500KBのファイル
    • 総バンドルサイズが500KB〜1MBのルート
    • 50KB〜100KBの外部パッケージ
  3. 継続的な改善:

    • 初回読み込みに含まれる100KB〜200KBのファイル
    • 総バンドルサイズが200KB〜500KBのルート
    • 10KB〜50KBの外部パッケージ

実践的な分析フロー

  1. 現状把握:

    • 各ルートの総バンドルサイズを確認します
    • ClientとServerのバンドルサイズを比較します(評価基準が異なることに注意)
    • ファイルタイプごとのサイズを確認します
  2. 問題の特定:

    • 目安を超えているファイルやルートを特定します
    • Import chainで依存関係を確認します
    • 大きな外部パッケージを特定します
  3. 最適化の実施:

    • 優先順位に従って最適化を実施します
    • 動的インポートやコード分割を検討します
    • 不要な依存関係を削除します
  4. 効果の確認:

    • 最適化後のバンドルサイズを確認します
    • パフォーマンス指標(Core Web Vitals)を確認します
    • 必要に応じて追加の最適化を実施します

実践的な分析のコツ

大きなバンドルを特定する

  1. Treemapで大きな矩形を探します
  2. 大きな矩形にマウスを合わせて、ツールチップでファイル名とサイズを確認します
  3. サイズが大きいファイルをクリックして、サイドバーで詳細情報を確認します

ルートごとのバンドルサイズを比較する

  1. 複数のルートを順番に選択して、それぞれのバンドルサイズを確認します
  2. Client/Serverを切り替えて、クライアントとサーバーのバンドルサイズを比較します(ただし、評価基準が異なることに注意)
  3. 大きなバンドルを持つルートを特定し、最適化の優先順位を決定します。Client bundleの最適化を優先し、Server bundleはSSRの応答速度・メモリ使用量の観点で評価します

ファイルタイプごとに分析する

  1. ファイルタイプフィルターを使って、JS、CSS、JSON、Assetを個別に表示します
  2. 各ファイルタイプのサイズを確認し、どのタイプが最も大きいかを把握します
  3. 特に大きなファイルタイプに焦点を当てて最適化します

依存関係を追跡する

  1. 検索機能を使って、特定のパッケージやモジュールを検索します
  2. そのパッケージがどのルートで使用されているかを確認します
  3. 複数のルートで使用されている共通の依存関係を特定します
  4. ファイルを選択して、サイドバーの「Import chain」セクションで依存関係を確認します

実践的な分析シナリオ

シナリオ1: 初回読み込み時間が遅い

問題: ユーザーが最初にページを開くまでの時間が長い

分析手順:

  1. 問題が発生しているページのルートを選択します
  2. Clientラジオボタンが選択されていることを確認します
  3. Treemap上で最も大きな矩形を特定します
  4. 大きな矩形をクリックして、サイドバーでファイルの詳細を確認します
  5. 「Import chain」セクションで、そのファイルがどの依存関係を持っているかを確認します

最適化のポイント:

  • 大きなファイルを特定し、それが本当に必要かどうかを検討します
  • 依存関係を確認し、不要な依存関係がないかを確認します
  • 大きなファイルを複数の小さなファイルに分割することで、初回読み込み時間を短縮できます

シナリオ2: 外部パッケージが大きすぎる

問題: 外部パッケージがバンドルサイズに大きく寄与している

分析手順:

  1. 検索ボックスにパッケージ名を入力します
  2. 検索結果がTreemap上でハイライト表示されることを確認します
  3. ハイライト表示されたファイルをクリックして、サイドバーで詳細を確認します
  4. 複数のルートを選択して、そのパッケージがどのルートで使用されているかを確認します

最適化のポイント:

  • より軽量な代替パッケージがないかを検討します
  • パッケージ全体ではなく、必要な部分だけをインポートします(Tree Shakingを活用)
  • Import chainでesmcjsを確認し、ES Modules形式のパッケージを使用しているか確認します
  • 使用頻度の低いパッケージは動的インポートを使用します

シナリオ3: サーバーサイドのバンドルサイズが大きい

問題: サーバーサイドのレンダリングが遅い、またはメモリ使用量が大きい

分析手順:

  1. Serverラジオボタンをクリックして、サーバーサイドのバンドルを表示します
  2. Treemap上で最も大きな矩形を特定します
  3. 大きな矩形をクリックして、サイドバーでファイルの詳細を確認します
  4. 「Import chain」セクションで、サーバーサイドで不要な依存関係がないかを確認します

最適化のポイント:

  • サーバーサイドで不要なクライアント専用コード(ブラウザAPIなど)が含まれていないかを確認します
  • データベースアクセスコードを最適化します
  • サーバーサイドで使用している外部ライブラリが本当に必要かどうかを検討します

シナリオ4: CSSバンドルのサイズが大きい

問題: CSSファイルのサイズが大きく、スタイルシートの読み込みが遅い

分析手順:

  1. CSSボタンをクリックして、CSSファイルのみを表示します
  2. 他のファイルタイプ(JS、JSON、Asset)を無効化します
  3. CSSファイルの中で最も大きな矩形を特定します
  4. 大きな矩形をクリックして、サイドバーでファイルの詳細を確認します

最適化のポイント:

  • 使用されていないCSSクラスやスタイルを削除します
  • CSS-in-JSを使用している場合、スタイルの重複がないかを確認します
  • Tailwind CSSを使用している場合、未使用のクラスを削除する設定を確認します

シナリオ5: 依存関係の重複を特定する

問題: 同じパッケージが複数の場所で重複してバンドルに含まれている

分析手順:

  1. 検索ボックスにパッケージ名やモジュール名を入力します
  2. 複数のルートを選択して、そのパッケージがどのルートで使用されているかを確認します
  3. ハイライト表示されたファイルをクリックして、サイドバーで詳細を確認します
  4. 「Output Chunks」セクションで、そのファイルが含まれるチャンクを確認します。複数のチャンクに含まれている場合、コード分割が適切に機能している可能性があります
  5. 「Import chain」セクションで、同じパッケージが複数のパスからインポートされていないか確認します
  6. 「Show all dependents」チェックボックスを有効化して、現在のルート外の依存関係も確認します。これにより、複数のルートで使用されている共通の依存関係を特定できます

最適化のポイント:

  • Output Chunksを活用: ファイルが複数のチャンクに含まれている場合、共通チャンクに適切に抽出されているかを確認します。共通チャンクに含まれていない場合、コード分割の最適化を検討します
  • Show all dependentsを活用: チェックボックスを有効にして、複数のルートで使用されている依存関係を特定します。これらの依存関係は共通チャンクに抽出する候補となります
  • Import chainで、同じパッケージの異なるバージョンが使用されていないかを確認します
  • package.jsonで依存関係を整理し、重複を排除します
  • 同じパッケージがesmcjsの両方からインポートされていないか確認します

experimental-analyzeと@next/bundle-analyzerの比較

experimental-analyzeと従来の@next/bundle-analyzerには、いくつかの重要な違いがあります。以下に主な違いをまとめます。

主な違い

項目 experimental-analyze @next/bundle-analyzer
ビルドツール Turbopack Webpack(legacy fallback)
実行方法 独立したコマンド(next experimental-analyze next buildプロセスに統合
ビルド成果物 本番用のbundleや静的ファイルは生成しない。.next/diagnostics/analyze以下に分析用ファイルのみ生成 実際のビルド成果物(.nextディレクトリ)を生成
実行時間 分析のみなので比較的速い フルビルドを実行するため時間がかかる
UI Next.js公式のBundle Analyzer UI @next/bundle-analyzerのUI(webpack-bundle-analyzerベース)
設定 設定不要(コマンド実行のみ) next.config.tsへの設定追加が必要
将来性 Next.jsチームが今後安定版に統合予定 Webpackはlegacy fallbackで、将来的に縮小・廃止される可能性が高い
Next.js 16での位置づけ Turbopackがデフォルトになったため、推奨される方法 --webpackフラグが必要で、legacy fallbackとして扱われる

機能面の違い

1. UIの違い

experimental-analyze:

  • Next.js公式のBundle Analyzer UI
  • ルート選択、Client/Server切り替え、ファイルタイプフィルター、検索機能など、Next.js App Routerに最適化されたUI
  • Output Chunks、Import chain、Show all dependentsなどの詳細な分析機能

@next/bundle-analyzer:

  • webpack-bundle-analyzerベースのUI
  • Webpackのバンドル構造を可視化
  • インタラクティブなTreemapとサイズ情報の表示

2. 実行方法の違い

experimental-analyze:

# 独立したコマンドとして実行
next experimental-analyze --serve

@next/bundle-analyzer:

# ビルドプロセスに統合
ANALYZE=true next build --webpack

3. ビルド成果物の違い

experimental-analyze:

  • 本番用のbundleや静的ファイルは生成しない
  • .next/diagnostics/analyze以下に分析用ファイル(Treemap UI、JSONデータなど)のみ生成
  • 分析のみを目的とした軽量な実行

@next/bundle-analyzer:

  • 実際のビルド成果物(.nextディレクトリ)を生成
  • 本番環境と同じビルドプロセスを実行
  • ビルドと分析を同時に実行

4. 分析対象の違い

experimental-analyze:

  • Turbopackを使用したビルドを分析
  • Next.js 16でデフォルトになったTurbopackのバンドル構造を可視化
  • App Routerのルート構造に最適化された分析

@next/bundle-analyzer:

  • Webpackを使用したビルドを分析
  • Webpackのチャンク分割とバンドル構造を可視化
  • --webpackフラグが必要(legacy fallback)

使い分けの指針

experimental-analyzeを使用する場合:

  • Next.js 16でTurbopackがデフォルトになったため、通常はこちらを使用
  • 開発中のバンドルサイズを素早く確認したい場合
  • ビルド成果物を生成せずに分析のみを行いたい場合
  • 将来性を考慮して、長期的に使用する方法を選びたい場合

@next/bundle-analyzerを使用する場合:

  • 既存のWebpackベースのプロジェクトで、Webpackのバンドル構造を確認したい場合
  • 実際のビルド成果物と同時に分析を行いたい場合
  • Turbopackへの移行前の比較分析を行いたい場合

注意: Next.js 16ではWebpackはlegacy fallbackとして扱われており、将来的にWebpack fallbackが縮小・廃止される可能性が高いです。長期的にはexperimental-analyzeの使用を推奨します。

併用する場合

両方を併用することで、TurbopackとWebpackのビルド結果を比較できます。ただし、Webpackはlegacy fallbackであり、将来的に縮小・廃止される可能性が高いため、比較は移行期間中のみ有効です。

# Turbopackビルドを分析
next experimental-analyze --serve

# Webpackビルドを分析(別のターミナルで実行)
ANALYZE=true next build --webpack

フォールバック:@next/bundle-analyzer

experimental-analyzeはTurbopackを使用したビルドを分析しますが、legacy Webpackビルドを分析する場合は、従来の@next/bundle-analyzerを使用できます。

注意: --webpackフラグはlegacy fallback機能です。Next.js 16ではWebpackはlegacy fallbackとして扱われており、今後はTurbopack側が第一級でメンテされていくと考えられます。将来的にWebpack fallbackが縮小・廃止される可能性も高いです。長期的にはexperimental-analyze(将来的に正式版として統合予定)の使用を推奨します。

インストール

# Bunを使用する場合(このプロジェクトの例)
bun add -D @next/bundle-analyzer

# npmを使用する場合
npm install --save-dev @next/bundle-analyzer

# yarnを使用する場合
yarn add -D @next/bundle-analyzer

# pnpmを使用する場合
pnpm add -D @next/bundle-analyzer

設定方法

next.config.tsに以下を追加:

import type { NextConfig } from 'next';

// eslint-disable-next-line @typescript-eslint/no-require-imports
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

const nextConfig: NextConfig = {
  // ... 既存の設定
};

export default withBundleAnalyzer(nextConfig);

package.jsonにスクリプトを追加

{
  "scripts": {
    "build:analyze": "ANALYZE=true next build --webpack"
  }
}

実行

# Bunを使用する場合(このプロジェクトの例)
bun run build:analyze

# npmを使用する場合
npm run build:analyze

# yarnを使用する場合
yarn build:analyze

# pnpmを使用する場合
pnpm build:analyze

2つの方法の使い分け

  • experimental-analyze: Turbopackを使用したビルドを分析する場合に使用。ビルド成果物を生成しないため、分析のみを実行します。プロダクション相当のビルドプロセスを分析用に実行します
  • @next/bundle-analyzer: legacy Webpackビルドを分析する場合に使用。実際のビルド成果物を生成します

推奨: 長期的にはexperimental-analyzeの使用を推奨します。Next.js 16ではWebpackはlegacy fallbackとして扱われており、今後はTurbopack側が第一級でメンテされていくと考えられます。将来的にWebpack fallbackが縮小・廃止される可能性も高いです。Turbopackがプロダクションビルドも置き換える段階で、experimental-analyzeが正式なanalyzerとして提供される見込みです。

両方を併用することで、TurbopackとWebpackのビルド結果を比較できます(ただし、Webpackはlegacy fallbackであり、将来的に縮小・廃止される可能性が高いため、比較は移行期間中のみ有効です)。

トラブルシューティング

ポートが既に使用されている場合

# 使用中のポートを確認(macOS/Linux)
lsof -i :4000

# Windowsの場合
netstat -ano | findstr :4000

# プロセスを停止(macOS/Linux)
pkill -f "next experimental-analyze"

# Windowsの場合、タスクマネージャーから該当プロセスを終了

別のポートを指定するか、使用中のプロセスを停止してください。

experimental-analyzeコマンドが見つからない場合

Next.jsがcanaryバージョンにアップグレードされているか確認:

# Bunの場合(このプロジェクトの例)
bunx next --version
# npmの場合: npx next --version
# yarnの場合: yarn next --version
# pnpmの場合: pnpm next --version
# canaryバージョンであることを確認(例: 16.1.0-canary.1)

canaryバージョンでない場合は、アップグレードしてください:

# Bunの場合
bun add next@canary

# npmの場合
npm install next@canary

# yarnの場合
yarn add next@canary

# pnpmの場合
pnpm add next@canary

Turbopackとの互換性

experimental-analyzeはTurbopack専用です。開発時にTurbopackが有効になっていることを確認してください。Webpackビルドを分析する場合は、@next/bundle-analyzerbuild:analyzeスクリプトを使用してください。

ディレクトリが見つからない場合

指定したディレクトリが存在しない、またはNext.jsプロジェクトでない場合、エラーが発生します。正しいディレクトリパスを指定してください。

まとめ

Next.js 16 + Turbopack環境でのバンドル分析は、experimental-analyzeコマンドを使用することで、Turbopackを使用したビルドを直接分析できるようになりました。このコマンドはnpm、yarn、pnpm、Bunなど、どのパッケージマネージャーでも使用できます。

主な特徴:

  • Turbopack専用: Turbopackを使用した本番相当のビルドプロセスを分析用に実行
  • ビルド成果物を生成しない: 本番用のbundleや静的ファイルは出力せず、.next/diagnostics/analyze以下に分析用の成果物だけを生成する
  • ブラウザで表示可能: --serveオプションで視覚的に確認可能
  • 柔軟な設定: ポート、マングリング、プロファイリングなどのオプションが利用可能
  • 将来性: Next.jsチームは今後安定版に統合する予定。Turbopackがプロダクションビルドも置き換える段階で、正式なanalyzerとして提供される見込み

重要な理解: experimental-analyzeは開発サーバーの内部状態を分析するものではなく、プロダクション相当のTurbopackビルドを出力なしで実行し、そのmodule graphを可視化するコマンドです。

experimental-analyzeと@next/bundle-analyzerの違い

experimental-analyzeは、従来の@next/bundle-analyzerと比較して以下の点が異なります:

  • ビルドツール: Turbopack(Next.js 16のデフォルト)vs Webpack(legacy fallback)
  • 実行方法: 独立したコマンド vs ビルドプロセスに統合
  • ビルド成果物: 分析用ファイルのみ生成 vs 実際のビルド成果物を生成
  • UI: Next.js公式UI(App Router最適化)vs webpack-bundle-analyzerベースのUI
  • 将来性: 今後安定版に統合予定 vs Webpackは将来的に縮小・廃止される可能性が高い

詳細な比較については、本文の「experimental-analyzeと@next/bundle-analyzerの比較」セクションを参照してください。

@next/bundle-analyzerと併用することで、TurbopackとWebpackのビルド結果を比較できます(ただし、Webpackはlegacy fallbackであり、将来的に縮小・廃止される可能性が高いため、比較は移行期間中のみ有効です)。

参考リンク

注意: experimental-analyzeコマンドは実験的機能のため、公式ドキュメントに詳細な記載がない場合があります。本記事の内容は、Next.js 16.1.0-canary.1での実際の動作確認に基づいています。

Discussion