フロントエンド

Next.js 16.3 Turbopack入門——CIキャッシュとビルド計測を整える

Next.js 16.3 PreviewのTurbopack改善をもとに、GitHub Actionsのキャッシュ、Firestoreへのビルド計測、移行判断の手順を実践形式で解説します。

2026年6月30日
Next.jsTurbopackGitHub ActionsFirestoreTypeScript
Next.js 16.3 Turbopack入門——CIキャッシュとビルド計測を整える

はじめに

Next.jsの開発で、こんな状態になっていませんか?

  • ローカルの next dev を長時間動かすとメモリ使用量が重くなる
  • GitHub Actionsの yarn build が遅く、PRレビューの待ち時間が伸びている
  • Turbopackが標準になったが、Webpack時代の設定が残っていて判断しづらい
  • 「速くなった気がする」だけで、移行可否を説明できるデータがない

2026年6月29日、Next.js公式ブログで Turbopack: What's New in Next.js 16.3 が公開されました。Next.js 16.3 Previewでは、開発サーバーのメモリ退避、next build 向けの永続ファイルシステムキャッシュ、実験的なRust React Compiler、import.meta.glob 対応などが案内されています。

本記事では、架空の業務管理SaaS「BuildBoard」を題材に、Next.js App Router + TypeScript + GitHub Actions + Firestoreで、Turbopack移行を感覚ではなく計測で進める手順を解説します。この記事を読み終えると、16.3 Previewを検証ブランチで試し、CIのビルド時間をFirestoreに残し、20〜30画面規模のアプリでも安全に判断できるようになります。

参考にした一次情報は次の通りです。

16.3 Turbopackで見るべき変更点

公式ブログでは、Turbopackの16.3 Previewを「CPU、メモリ、ビルド時間、ランタイム体験の改善」として説明しています。実務で最初に確認したいのは、次の4点です。

変更点何を見るかBuildBoardでの判断
devメモリ退避長時間の next dev でメモリが増え続けないか30画面を触った後の開発体験
build用永続キャッシュCIの2回目以降が速くなるかPRごとの待ち時間削減
Rust React CompilerReact Compiler有効時のビルド時間大きいUIモジュールだけ別途検証
import.meta.globMDXや設定ファイルの一覧読み込みブログ、ヘルプ、商品説明の取り込み

重要なのは、16.3はPreviewである点です。公式ブログでも @preview タグで試せると案内されており、安定版は今後のリリースとして扱われています。いきなり本番ブランチへ入れるのではなく、検証ブランチでビルドと開発体験を切り分けて見ます。

事前準備: 検証ブランチでPreviewを入れる

BuildBoardでは、受注、請求、在庫、監査ログなど25画面ほどある想定にします。まずは依存更新だけのブランチを切ります。

git switch -c chore/nextjs-16-3-turbopack-check
yarn add next@preview
yarn build

Next.jsのTurbopack API Referenceでは、Next.js 16以降はTurbopackが標準バンドラで、通常は next devnext build だけで利用できると説明されています。Webpackへ戻す必要がある場合は --webpack を使います。

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "build:webpack": "next build --webpack"
  }
}

Webpack固有の webpack() 設定、Webpack plugin、sassOptions.functions、古いCSS Modules記法を使っている場合は、Turbopackで同じ挙動にならない可能性があります。まずは失敗したビルドログを保存し、対象を「設定」「CSS」「外部パッケージ」「App Routerの構成」に分類します。

失敗箇所最初に見るもの
next.config.tswebpack() 設定が残っていないか
CSS Modules:local:globalcomposes の使い方
SassJavaScriptのカスタム関数を使っていないか
linked packageプロジェクト外のファイルを参照していないか

ハンズオン1: build用ファイルシステムキャッシュを試す

Next.js 16.3 Previewでは、next build でもTurbopackの永続ファイルシステムキャッシュを使えるようになりました。公式ブログでは、CIで .next ディレクトリを前回実行から引き継ぐと、Turbopackがビルド開始時にディスク上のキャッシュを読むと説明されています。

検証では、build用キャッシュを明示的に有効化します。

// next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  experimental: {
    turbopackFileSystemCacheForBuild: true,
  },
};

export default nextConfig;

既存の output: "export"images.unoptimizedtrailingSlash などがある場合は消さずに、experimental だけを追加します。記事のために単純化していますが、実プロジェクトでは設定を置き換えないことが大事です。

// 既存設定へ足すイメージ
const nextConfig: NextConfig = {
  output: "export",
  images: {
    unoptimized: true,
  },
  experimental: {
    turbopackFileSystemCacheForBuild: true,
  },
};

ここで1回のビルドだけを見ても判断できません。キャッシュは2回目以降に効くため、最低でも「cold build」「同一コミットのwarm build」「ソース変更後のwarm build」を分けます。

ハンズオン2: GitHub Actionsで .next/cache を復元する

Next.jsのCI Build Cachingガイドでは、CIで .next/cache を永続化する構成が案内されています。GitHub Actionsでは actions/cache@v4 を使います。BuildBoardでは、YarnのキャッシュとNext.jsのキャッシュを分けて見えるようにします。

name: turbopack-build-check

on:
  pull_request:
    branches: [main]
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: yarn

      - name: Restore Next.js cache
        uses: actions/cache@v4
        with:
          path: |
            .next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('yarn.lock') }}-${{ hashFiles('src/**/*.ts', 'src/**/*.tsx', 'content/**/*.mdx', 'next.config.ts') }}
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('yarn.lock') }}-

      - run: yarn install --frozen-lockfile

      - name: Build and measure
        run: |
          started_at=$(date +%s)
          yarn build
          finished_at=$(date +%s)
          echo "build_seconds=$((finished_at - started_at))" >> "$GITHUB_OUTPUT"
        id: build

hashFiles には、アプリのソースだけでなく content/**/*.mdx も含めています。BuildBoardではヘルプ記事や業務マニュアルをMDXで管理している想定なので、記事変更でビルド成果物が変わるためです。

ハンズオン3: Firestoreへビルド計測を保存する

CIが速くなったかをチームで説明するには、Actionsのログだけでは弱いです。Firestoreに buildMetrics コレクションを作り、PR番号、コミット、build秒数、cache keyを保存します。

// src/lib/build-metrics/saveBuildMetric.ts
import { FieldValue, getFirestore } from "firebase-admin/firestore";

export type BuildMetric = {
  workflow: string;
  runId: string;
  ref: string;
  sha: string;
  buildSeconds: number;
  nextVersion: string;
  cacheKey: string;
};

export async function saveBuildMetric(metric: BuildMetric) {
  await getFirestore().collection("buildMetrics").add({
    ...metric,
    createdAt: FieldValue.serverTimestamp(),
  });
}

Actionsから直接Firestoreへ書く場合は、サービスアカウントキーを長期secretとして置くより、OIDCでGoogle Cloudへ認証する構成を優先します。ここでは保存APIをNext.js側に用意し、Actionsは短い同期トークンだけを持つ構成にします。

// src/app/api/admin/build-metrics/route.ts
import { saveBuildMetric } from "@/lib/build-metrics/saveBuildMetric";

export async function POST(request: Request) {
  const auth = request.headers.get("authorization");

  if (auth !== `Bearer ${process.env.BUILD_METRICS_TOKEN}`) {
    return Response.json({ error: "unauthorized" }, { status: 401 });
  }

  const body = (await request.json()) as {
    workflow: string;
    runId: string;
    ref: string;
    sha: string;
    buildSeconds: number;
    nextVersion: string;
    cacheKey: string;
  };

  await saveBuildMetric(body);
  return Response.json({ ok: true });
}

GitHub Actions側では、ビルド後にこのAPIへ送ります。

      - name: Save build metric
        env:
          ENDPOINT: ${{ secrets.BUILD_METRICS_ENDPOINT }}
          TOKEN: ${{ secrets.BUILD_METRICS_TOKEN }}
          BUILD_SECONDS: ${{ steps.build.outputs.build_seconds }}
        run: |
          curl -fsS -X POST "$ENDPOINT" \
            -H "Authorization: Bearer $TOKEN" \
            -H "Content-Type: application/json" \
            -d "{
              \"workflow\":\"$GITHUB_WORKFLOW\",
              \"runId\":\"$GITHUB_RUN_ID\",
              \"ref\":\"$GITHUB_REF\",
              \"sha\":\"$GITHUB_SHA\",
              \"buildSeconds\":$BUILD_SECONDS,
              \"nextVersion\":\"next@preview\",
              \"cacheKey\":\"${{ runner.os }}-nextjs\"
            }"

20〜30画面規模のアプリでは、1回速いだけで採用しないほうが安全です。最低でも5回分のPR、または workflow_dispatch で同一コミットを3回回し、中央値を見ます。

ハンズオン4: import.meta.glob をMDX一覧で試す

16.3のTurbopackはVite互換の import.meta.glob APIをサポートします。公式ブログでは、ファイル名をハードコードせず、条件に合うモジュール群をオブジェクトとして取得できると説明されています。

BuildBoardでは、業務ヘルプのMDXを一覧化する用途で試します。

// src/lib/help/loadHelpModules.ts
type HelpModule = {
  metadata: {
    title: string;
    section: "orders" | "billing" | "inventory";
  };
};

const modules = import.meta.glob<HelpModule>("../../content/help/*.mdx", {
  eager: true,
});

export function listHelpPages() {
  return Object.entries(modules).map(([path, module]) => ({
    path,
    title: module.metadata.title,
    section: module.metadata.section,
  }));
}

注意点は、import.meta.glob はTurbopack機能であり、--webpack でビルドするアプリでは動かないことです。Webpack fallbackを残す移行期間に共通コードへ入れると、fallback側の検証を壊します。まずはTurbopack専用の小さい領域で試し、採用判断後に広げます。

判断基準を決めてから採用する

BuildBoardでは、次の基準を満たしたらNext.js 16.3 Previewの検証結果を採用候補にします。

指標採用ライン
yarn build5回中5回成功
warm build中央値現行比で20%以上短縮
ローカルdev30画面を触ってもメモリ増加が頭打ちになる
fallbacknext build --webpack が必要な理由をIssue化済み
互換性CSS、MDX、Firestore管理画面で表示崩れなし

移行タスクは TB-001 から TB-030 のように架空チケットで分割します。たとえば TB-001 は依存更新、TB-006 はCSS Modules確認、TB-014 はFirestore管理画面、TB-022 はCIキャッシュ、TB-030 は採用判断という粒度です。実際の顧客名、売上、内部プロジェクト名は記事やログに含めません。

まとめ

Next.js 16.3 PreviewのTurbopack改善は、単なる「速いらしい」という話ではなく、開発サーバーのメモリ、CIのbuildキャッシュ、React Compiler、MDXの取り込み方まで影響します。

実務では、次の順番で進めるのが堅実です。

  • 検証ブランチで next@preview を入れる
  • turbopackFileSystemCacheForBuild を有効化してwarm buildを見る
  • GitHub Actionsで .next/cache を復元する
  • build秒数をFirestoreへ保存し、中央値で判断する
  • import.meta.glob などTurbopack専用機能はfallback期間に広げすぎない

Turbopack移行は、設定を書き換える作業というより、開発とCIのボトルネックを計測可能にする作業です。PR待ち時間が長いチームほど、まずは1本の検証ワークフローを作り、数字で採用判断できる状態にしておく価値があります。