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

はじめに
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 Compiler | React Compiler有効時のビルド時間 | 大きいUIモジュールだけ別途検証 |
import.meta.glob | MDXや設定ファイルの一覧読み込み | ブログ、ヘルプ、商品説明の取り込み |
重要なのは、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 dev と next 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.ts | webpack() 設定が残っていないか |
| CSS Modules | :local、:global、composes の使い方 |
| Sass | JavaScriptのカスタム関数を使っていないか |
| 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.unoptimized、trailingSlash などがある場合は消さずに、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 build | 5回中5回成功 |
| warm build中央値 | 現行比で20%以上短縮 |
| ローカルdev | 30画面を触ってもメモリ増加が頭打ちになる |
| fallback | next 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本の検証ワークフローを作り、数字で採用判断できる状態にしておく価値があります。