はじめて読む方へ: この記事では、Next.js 16.3 の変更点を「速くなったらしい」で終わらせず、どこが自動で効く改善で、どこからが開発者の設計判断を必要とする改善なのかを分けて整理します。まずは アップグレードするだけで効く改善 と、Instant Navigations を有効化して画面遷移を速くする改善 の2階建てだと理解してください。
2026年8月3日にリリースされた Next.js 16.3 は、公式が「16.0(2025年11月)以来もっとも大きなアップデート」と表現しているリリースです。目玉は Instant Navigations(インスタント・ナビゲーション) という、リンククリックの体感速度をSPA並みに引き上げるための機能群です。
ただし16.3の中身はそれだけではありません。何も書き換えなくてもアップグレードするだけで効く改善(開発時メモリ最大90%削減、ビルドキャッシュ、SSRスループット +22% など)と、設定フラグで明示的にオプトインする新しいナビゲーションモデルの2階建てになっています。
本記事はこの2階建てを分けて整理し、それぞれ「何が変わるのか」「自分のプロジェクトで今すぐ入れるべきか」を判断できるようにまとめたものです。
情報源は Vercel 公式ブログの以下2本です。数値ベンチマークはすべて Vercel 自身が自社プロダクト(vercel.com、nextjs.org、v0)で計測したものであり、第三者検証ではない点は割り引いて読んでください。
- Next.js 16.3 リリースノート: https://nextjs.org/blog/next-16-3
- Next.js 16.3: Instant Navigations: https://nextjs.org/blog/next-16-3-instant-navigations
本題に入る前に、この記事で頻出する用語を先に定義しておきます。すでにご存じの方は読み飛ばしてください。
| 用語 | 意味 |
|---|---|
| App Router | Next.js 13 以降の新しいルーティング方式。app/ ディレクトリ配下にページを置く。従来の pages/ ベースは Pages Router と呼ばれる |
| Server Components(サーバーコンポーネント / RSC) | サーバー側だけで実行され、実行結果(描画済みのUI)だけをクライアントに送るReactコンポーネント。ブラウザに送るJavaScriptを減らせる一方、描画にサーバーとの往復が必要になる |
| SPA(Single Page Application) | 最初にアプリ全体のJavaScriptを読み込んでしまい、以降の画面遷移をブラウザ内部の状態変更だけで行う方式。遷移が「一瞬」に感じられるのが最大の利点 |
| prefetch(プリフェッチ) | ユーザーがまだクリックしていないリンク先を、事前に裏で取得しておくこと。クリック時点で手元にデータがあるので遷移が速くなる |
| shell(ローディングシェル) | ページの「骨格」だけのUI。ヘッダー・サイドバー・見出し・スケルトン表示など、データが揃っていなくても描画できる部分を指す。中身は後から流し込まれる |
<Suspense> | Reactの機能。子コンポーネントのデータ取得が終わるまで、代わりのUI(fallback)を表示しておく境界(バウンダリ)を作る |
'use cache' | Next.js 16 で導入されたキャッシュ用ディレクティブ。関数やコンポーネントの先頭に書くと、その結果がキャッシュ対象になる。従来の暗黙的なキャッシュを廃し、「キャッシュしたいものを明示的に宣言する」方式 |
| Cache Components | 'use cache' を土台にした新しいキャッシュ/レンダリングモデル全体の呼称。cacheComponents: true で有効化する |
| Turbopack | Vercel が Rust で開発している Next.js のバンドラ(webpack の後継)。開発サーバーとビルドの高速化を担う |
| ISR(Incremental Static Regeneration) | 静的生成したページを、デプロイし直さずにバックグラウンドで再生成して差し替える仕組み |
| エラーバウンダリ | 配下のコンポーネントで発生した例外を捕捉し、アプリ全体を落とさずに代替UIを表示するReactの仕組み |
まずはコードを1行も変えずに恩恵が得られる部分から。公式も「全アプリに16.3へのアップグレードを推奨」というスタンスです。
npm install next@latest
next dev を長時間動かしっぱなしにするとメモリが際限なく膨らんでいく——これは多くの人が経験してきた問題だと思います。16.3 では Turbopack に ディスクキャッシュ(16.1で先行導入)と メモリエビクション(不要になったデータをメモリから追い出す仕組み) の2つがデフォルト有効になりました。
公式が示す実測値(50ルートをコンパイルした後のメモリ使用量):
| プロジェクト | Before | After | 削減率 |
|---|---|---|---|
| vercel.com(ダッシュボード) | 21.5 GB | 2 GB | 約90%減 |
| nextjs.org | 4,600 MB | 840 MB | 約82%減 |
21.5GB というのがそもそも凄まじいのですが、大規模アプリでは「開発機のメモリが足りずに next dev が落ちる」という話が実際にあったわけで、それが2GBまで落ちるインパクトは大きいです。
設定の詳細は turbopackMemoryEviction のドキュメントを参照してください。
これまで開発時にしか効かなかった Turbopack のディスクキャッシュが、next build でもデフォルト有効になりました。変更されていない成果物はキャッシュから読み出されるため、2回目以降のビルドが速くなります。
公式のビルド時間比較:
| プロジェクト | Cold(キャッシュなし) | Cached | 改善 |
|---|---|---|---|
| nextjs.org | 21秒 | 9.2秒 | 約2.3倍 |
| vercel.com/home | 66秒 | 46秒 | 約1.4倍 |
| vercel.com/geist | 30秒 | 5.5秒 | 約5.5倍 |
注意点として、この効果はCI環境で「前回ビルドのキャッシュディレクトリが引き継がれること」が前提です。GitHub Actions などでコンテナが毎回まっさらになる構成だと、キャッシュの永続化(actions/cache などでのキャッシュディレクトリ保存)を自分で設定しない限り、常に Cold のままです。ここは導入時に必ず確認すべきポイントです。
TypeScript 7(TypeScriptコンパイラをGoでネイティブ移植したバージョン。従来比で約10倍高速とされる)がリリースされたことを受け、next build 時の型チェックに TypeScript 7 を使えるようになりました。プロジェクトの依存を上げるだけです。
pnpm add -D typescript@^7
大規模プロジェクトほど型チェックがビルド時間の支配項になりがちなので、ここは効く可能性が高いです。
App Router のレンダリング層で使っていた Web Streams(ブラウザ標準のストリームAPI)を、Node.js ネイティブのストリームに置き換えたという変更です。サーバーサイドレンダリング(SSR)中に両者を相互変換していたオーバーヘッドが消えた、というのが理由です。
結果として、負荷時に最大22%多くのリクエストを捌けるとのこと。アプリ側のコード変更は不要です。地味ですが、SSRを本番で回している環境にとってはインフラコストに直結する改善です。
個人的にはこれが隠れた目玉だと思っています。
Claude Code や Cursor のようなAIコーディングエージェントは、学習データが古いと 存在しないAPIや旧バージョンの書き方を平気で提案してくる という問題を抱えています。Next.js は特にバージョン間の差分が大きいフレームワークなので、この被害が大きい領域でした。
16.3 では、next dev を実行すると AGENTS.md(プロジェクト直下に置く、AIエージェント向けの指示書ファイル。多くのコーディングエージェントが自動で読み込む慣習になっている)に、プロジェクトが実際に使っているバージョンのドキュメントへのパスを書き込み、維持してくれるようになりました。参照先はローカルの node_modules にバンドルされたドキュメントです。
つまり、エージェントは「インターネット上の最新記事」でも「学習時点の古い知識」でもなく、手元のバージョンと完全に一致したドキュメントを読むようになります。この仕組みが入ったことで、Vercel は同じ目的で提供していた従来の Skills(vercel-labs/next-skills)を役目終了として引退させています。
一定のサイズ以下のprefetchは自動的にまとめて1リクエストに束ねられるようになりました。ただし、複数ルートで再利用できる大きめの共有セグメントは、再利用性を保つために引き続き別リクエストのままです。
内容が変わらない(immutable な)静的アセットを、デプロイをまたいで再利用できるようになりました。内容が不変である以上、いわゆるスキュー(新旧デプロイが混在して、古いHTMLが新しいアセットを参照して壊れる問題)が起きないため安全、という理屈です。
catchError)従来のNext.jsでは、Reactのエラーバウンダリが notFound() や redirect() を呼ぶアプリケーションコードと干渉するという問題がありました。notFound() は内部的に例外を投げて実現されているため、自前のエラーバウンダリがそれを「エラー」として拾ってしまう、というものです。
さらに、リセットできるのはクライアント側の状態だけで、レンダリング中に失敗したServer Componentを再取得する手段がありませんでした。
16.3 では next/error の catchError を使って、これらを解決したカスタムエラーバウンダリを定義できます。
'use client';
import { catchError, type ErrorInfo } from 'next/error';
function Fallback(props: { title: string }, { error, retry }: ErrorInfo) {
return (
<div>
<h2>{props.title}</h2>
<p>{error.message}</p>
{/* retry() で配下を再フェッチ = Server Component の再レンダリングも含む */}
<button onClick={() => retry()}>再試行</button>
</div>
);
}
export default catchError(Fallback);
ポイントは第2引数で渡ってくる retry()。これを呼ぶと、そのバウンダリ配下の子を再フェッチします。Server Componentの再レンダリングも含まれるため、「一時的なDBエラーで落ちた画面を、ユーザーのボタン1つで復帰させる」といった実装が素直に書けるようになりました。
import.meta.glob(globインポート)Turbopack が Vite 互換の import.meta.glob API に対応しました。ファイルシステム上の複数モジュールをまとめて読み込めます。
import matter from 'gray-matter';
export default function Page() {
// 第2引数 eager: true で即時読み込み
const posts = import.meta.glob('./posts/*.md', { eager: true });
return (
<ul>
{Object.entries(posts).map(([path, mod]) => (
<li key={path}>{matter(mod.default).data.title}</li>
))}
</ul>
);
}
ローカルのMarkdownを読んでブログを構成する、といったよくあるパターンで、HMR(ホットモジュールリロード:ファイルを保存すると画面が即座に更新される仕組み)が効くようになるのが実利です。従来 fs.readdir で書いていた箇所は、ファイル追加時に手動リロードが必要でした。
なお .md を読むには next.config.js 側でローダー登録が必要です。
next/root-params)これまで動的ルートのparams(URL中の可変部分)にアクセスするには、ページからpropsとして下位コンポーネントに渡していくしかありませんでした。app/[lang]/... のようなルートレイアウトより上位で定義されたparamsは事実上グローバルな値なのに、コンポーネントツリーの深い場所までprops のバケツリレー(prop drilling)を強いられていたわけです。
16.3 では、こうした「root params」を任意のServer Componentから直接読めます。
import { lang } from 'next/root-params';
export default async function PostPage(props: PageProps<'/[lang]/posts/[slug]'>) {
const { slug } = await props.params;
const language = await lang(); // props経由でなくても取得できる
return <article>{language} / {slug}</article>;
}
i18n(多言語対応) のように、共通ユーティリティや深い階層のコンポーネントが現在の言語を必要とするケースで効きます。'use cache' スコープの内側でも動作します。
現時点でのサポートはServer Componentのみで、Route HandlerとServer Actionへの対応は今後のリリース予定とされています。
ここからがオプトイン(明示的に有効化する)機能です。
サーバー駆動のアプリでは、画面遷移は基本的にネットワーク往復を伴います。
ニュースサイトやブログのようなコンテンツ主体のサイトなら、これでも問題ありません。しかし「アプリらしさ」を求められるプロダクトでは、この2の空白が致命的にもっさり感じられます。
一方、SPAはこうです。
Server Componentsは「送るJavaScriptを減らす」「ネットワークのウォーターフォール(データ取得が数珠つなぎになって遅くなる現象)を避ける」という点で成功しましたが、代わりに遷移が遅く感じられるという副作用を生みました。16.3 はここに正面から取り組んだリリースです。
// next.config.ts
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
cacheComponents: true,
partialPrefetching: true,
};
export default nextConfig;
この2つは将来のメジャーバージョンでデフォルトになる予定と明言されています。つまり今オプトインするのは「実験的な寄り道」ではなく「先行移行」です。
cacheComponents を有効にすると、ルートがサーバー側で何かを await している場合、開発時に明示的な選択を迫られるようになります。
<Suspense> で包む) — ローディング状態を即座に見せ、残りのUIを後から流し込む'use cache' を付ける) — 以前キャッシュしたUIを即座に見せる(リクエスト間で再利用)export const instant = false;) — このルートは意図的にサーバー待ちにする前2者を選べば遷移はSPAのように「即座」になります。3つ目は明示的なオプトアウトで、例えばブログ記事のように「中途半端なシェルを見せるくらいなら完全な内容が揃うまで待たせたい」というケース向けです。ページやレイアウトに1行書けば警告は消えます。
重要なのは、フレームワークが勝手に決めるのではなく、開発者が意思決定する構図になっている点です。従来の暗黙的キャッシュへの反省が反映されています。
とはいえ「全ルートについて漏れなく判断する」のは人間には無理があります。従来も loading.tsx を各ルートに置けば同じことはできましたが、どこか1つ置き忘れて遅い遷移が残るのが常でした。
そこで追加されたのが Instant Insights です。Next.js DevTools のパネルとして、即座でない遷移を開発時にエラーとして自動的に表示します。
さらに各インサイトには、そのまま貼り付けられるAIエージェント向けプロンプトが添付されます。「この警告を消すにはどう直すか」をエージェントに教える形になっているわけで、前述のAGENTS.md対応と合わせて、Vercelが本気でエージェント前提の開発体験を設計しにきているのが分かります。
既知の問題として、Safari では Instant Insights の挙動に不具合があるとアナウンスされています。開発時は Chrome か Firefox の利用が推奨されています。
ここが個人的に一番面白い変更点です。
従来の問題: Next.js は、ビューポートに入ったリンクごとにprefetchリクエストを飛ばしていました。本番モードでNetworkタブを開いてスクロールすると、リクエストが滝のように流れていくアレです。公式も「ridiculous(ばかげている)に見えるという指摘はもっともだ」と認めています。
例えばサイドバーにチャットのリンクが20本あれば、20本のprefetchリクエストが飛んでいました。リンク先はすべて /chat/[id] という同じルートなのにです。
16.3のアプローチ: SPAのやり方を借りました。SPAが遷移を即座にこなせるのは、次の画面を描画するのに必要なコードをあらかじめバンドルして持っているからです。データはまだ足りないかもしれませんが、シェルを描くには十分です。
そこで Next.js も、リンクごとではなくルートごとに、再利用可能なシェルを1回だけprefetchする方式に変えました。取得したシェルはクライアント側にキャッシュされ、セッション中は使い回されます。
/chat/1, /chat/2, ... /chat/20 → 20リクエスト/chat/[id] のシェル → 1リクエスト概念的には、SPAにおけるルート単位のコード分割とほぼ同じ発想です。
それでも「シェル以上」が欲しいとき は、リンク側で <Link prefetch={true}> を指定します。ただしこの場合でも、Next.js はルート全体を最深部までレンダリングしようとはしません。同期的に取得できるもの/URLから判明するもの(paramsやsearchParams)/'use cache' が付いているもの、ここまでを描画対象とします。
つまり、従来の「全部prefetchするか、しないか」というオール・オア・ナッシングの選択が解消されたわけです。シェルがベースラインとしてあり、その上に必要なリンクだけ厚みを足せます。
generateStaticParams(ビルド時に静的生成する動的ルートのパラメータ一覧を返す関数)で一部のページだけを事前生成している場合、残りのページには従来トレードオフがありました。
16.3 では両取りできます。事前生成していないページは、初回訪問時に即座にローディングシェルを返し、裏で完全な事前生成へアップグレードします。2人目以降の訪問者はキャッシュから完成品を受け取ります。
トラフィックに応じてアップグレード頻度を制御するAPIも検討中とのことです。
Next.js は開発時にはprefetchを無効化しているため、「ユーザーが実際に見るローディングの流れ」を開発中に確認するのが困難でした。
Navigation Inspector は、ページロードやクライアント遷移をシェルの状態で一時停止できるDevToolです。「Resume」を押すと完成状態に進みます。これにより「このルートのどこまでが即座に出て、どこからがネットワーク待ちなのか」を視覚的に切り分けられます。
instant() ヘルパー — デグレを防ぐもう1つのよくある失敗パターンが、今日は即座に遷移するページが、明日には遅くなっているというものです。原因は地味です。
cookies() を読むコンポーネントが追加され、ルートがリクエスト時レンダリングに降格した<Suspense> 境界が移動し、ページの一部がブロッキングになったいずれも「見た目は動いているのでレビューでは気づかない」種類の劣化です。
そこで @next/playwright の instant() ヘルパーが追加されました。遷移の瞬間に何が見えているべきかをテストとして固定できます。
import { expect, test } from '@playwright/test';
import { instant } from '@next/playwright';
test('商品タイトルは即座に表示される', async ({ page }) => {
await page.goto('/products/shoes');
// ネットワーク待ちをせずに、この時点で見えるべきものを検証する
await instant(page, async () => {
await page.click('a[href="/products/hats"]');
await expect(page.locator('h1')).toContainText('Baseball Cap');
await expect(page.getByText('Checking inventory...')).toBeVisible();
});
// ここから先はデータ到着後
await expect(page.getByText('12 in stock')).toBeVisible();
});
パフォーマンスを「感覚」ではなくE2Eテストの成否として扱えるようにした点が本質で、CIに組み込めば体感速度のデグレをPR単位で止められます。
既存プロジェクトを移行する場合、公式は Cache Components 移行ガイドと、AIエージェントに移行作業を任せるための Skill を用意しています。移行時には dynamic / revalidate / dynamicParams といった、Cache Components 下では使えなくなるセグメント設定の除去が必要になります。
React Compiler は、useMemo や useCallback による手作業のメモ化を不要にするため、ビルド時にコンポーネントを自動最適化するツールです。
これまでは Babel(JavaScriptのトランスパイラ)経由でNode.js上で動かす必要がありました。実験的なRust移植版は Turbopackの内部で直接動作するため、コードを生成して再パースする往復が消えます。
// next.config.ts
const nextConfig: NextConfig = {
reactCompiler: true,
experimental: {
turbopackRustReactCompiler: true,
},
};
v0 のような大規模アプリでの計測では、next dev からページが表示可能になるまでの時間がコールドビルドで34%、ウォームビルドで46%短縮されたとのこと。
ただしこの数値はBabelを完全に外していることが前提です。他のトランスフォームでBabelを併用し続けている場合、効果は小さくなります。ここは自分の環境で条件を確認しないと期待外れになりやすい部分です。
useOffline)通常、ネットワークが切れるとソフトナビゲーション・データフェッチ・Server Action は例外を投げます。experimental.useOffline を有効にすると、Next.js はそれらをエラーにせず保留状態のまま維持し、接続が戻ったタイミングで再試行します。
'use client';
import { useOffline } from 'next/offline';
export function OfflineBanner() {
const isOffline = useOffline();
if (!isOffline) return null;
return <div>オフラインです。再接続後に自動で再試行します。</div>;
}
前述の Partial Prefetching がルートのシェルをクライアントにキャッシュしているため、prefetch済みのルートはオフラインでもシェルまでは描画され、再接続時にデータが流し込まれます。モバイル回線で使われる業務アプリなどでは、かなり実用的な組み合わせになりそうです。
ここからは公式の記述ではなく、筆者の整理です。
すぐ上げてよい部分(Part 1): 開発メモリ・ビルドキャッシュ・Node.jsストリーム・TypeScript 7 は、いずれもコード変更を伴わない性能改善です。16.x 系にいるなら上げない理由が薄い。ただしビルドキャッシュはCI側の設定が伴わないと効果が出ない点に注意。
慎重に評価すべき部分(Part 2): Instant Navigations は「速くなる魔法」ではありません。実態は、各ルートについて Stream / Cache / Block のどれかを開発者が選ぶことを強制する仕組みです。裏を返せば、レイアウトとデータ取得がきれいに分離されていないコードベースでは、移行時に大量の設計判断が発生します。効果が出るのも、そもそも遷移でサーバー待ちが発生しているアプリに限られます。全ページが完全に静的でCDN配信されているサイトでは、体感差はほぼ出ません。
したがって現実的な進め方は、いきなり全面適用ではなく、トラフィックの多い2〜3ルートで試し、Navigation Inspector で実際のシェルを確認し、instant() テストで固定する——という順序になると思います。
また測定は、開発機のノートPCではなくスロットリングした回線・中位のAndroid端末で行うべきです。ナビゲーション系の機能は、高速な開発環境では常に素晴らしく見えます。
cacheComponents + partialPrefetching の2フラグで有効化。将来のデフォルトinstant() テストヘルパーという、検出・可視化・回帰防止の3点セットが揃っているのが今回の設計の巧さ