本記事は TypeScript + Next.js 学習リポジトリ の実装をベースに、Next.js App Router の キャッシュ機構 を体系的に解説します。
はじめに
Next.js は「速い」と評価されますが、その理由のひとつが 強力なキャッシュ機構 です。
ただ、その仕組みを正しく理解していないと「データを更新したのに古いまま表示される…」とハマりがちです。
本記事では、以下の流れでNext.jsのキャッシュ管理を解説します。
- 4種類のキャッシュ機能
- Data Cache の設定方法
use cacheで簡単にキャッシュする- Revalidation でキャッシュを更新する
router.refresh()でブラウザ側のキャッシュを削除する
1. 4種類のキャッシュ機能
Next.js には主に4つのキャッシュ機能があります。
| 機能 | 保存先 | 自動有効 | 概要 |
|---|---|---|---|
| Full Route Cache | サーバー | ✅ | ビルド時にコンポーネント関数が実行され、HTMLなどの静的ファイルをキャッシュに保存。アクセス時には関数は実行されず、保存済みのものを返すだけ |
| Router Cache | ブラウザ | ✅ | ユーザーが開いたページをキャッシュに保存し「戻る・進む」が瞬時に描画。さらに画面内のリンクが見えた時点で先読みする |
| Request Memorization | サーバー | ✅ | 同じAPIに対して1回のリクエスト内で重複したアクセスがあった場合、それを1つにまとめる |
| Data Cache | サーバー | ❌(設定が必要) | APIから取得したデータをキャッシュに保存し、同じAPIへのアクセス時には保存したデータを返す |
イメージ図
[ブラウザ]
┃ Router Cache(ブラウザに保存)
┃ ── 戻る/進む/プリフェッチを高速化
▼
[Next.js サーバー]
┃ Full Route Cache (HTMLそのもの)
┃ Data Cache (API取得データ)
┃ Request Memorization (1リクエスト内の重複排除)
▼
[外部API / DB]
このうち Data Cache だけは明示的な設定が必要 なので、まずはここから見ていきます。
2. Data Cache の設定方法
概要
あるAPIからデータを取得するとき、取得したデータをサーバーにキャッシュして、同じAPIであれば リクエストを跨いで キャッシュデータを参照し、高速なレスポンスを行う機能。
「Request Memorization」が 1リクエスト内 での重複排除なのに対し、「Data Cache」は 複数リクエストにまたがって キャッシュするのが違いです。
使用方法
fetch() の第2引数に cache: 'force-cache' を指定するだけで有効化できます。
const handleSubmit = async (e: SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
const form = new FormData(e.currentTarget);
const name = form.get('name');
await fetch('/api/create', {
cache: 'force-cache', // ← これにより Data Cache が有効化される
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name }),
});
};
最初のリクエスト時にサーバーへキャッシュされ、以降は同じURLへのアクセスはキャッシュから返されるようになります。
3. use cache で簡単にキャッシュする
概要
fetch() 以外の処理(DB直接アクセス、重い計算処理など)もキャッシュしたい場合に便利なのが use cache ディレクティブです。
Data Cache をより簡単に実装できる機能 という位置付けです。
使い方
① next.config.ts に設定を追記
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
/* config options here */
cacheComponents: true, // ← これを追記する
};
export default nextConfig;
② キャッシュしたい関数のトップレベルに 'use cache' と書く
async function getHeavyData() {
'use cache'; // ← この一行を追加
await new Promise((resolve) => setTimeout(resolve, 3000));
return '重いデータの取得完了';
}
実装例(app/use-cache/page.tsx)
import { cacheTag, revalidatePath, revalidateTag } from 'next/cache';
async function getHeavyData() {
'use cache';
await new Promise((resolve) => setTimeout(resolve, 3000));
return '重いデータの取得完了';
}
export default async function useCache() {
const data = await getHeavyData();
return (
<>
<h1>{data}</h1>
</>
);
}
このページは初回アクセスでは3秒かかりますが、2回目以降はキャッシュから瞬時に返されます。
💡
loading.tsxを併用すれば、初回ロード時に「Loading...」が表示されてUXが向上します。
4. Revalidation でキャッシュを更新する
概要
サーバー側のキャッシュを 削除・更新 するための仕組み。
主に Full Route Cache と Data Cache に対して行う。Request Memorization は1リクエスト内で保存・削除されるため対象外。
例えば「記事投稿サイトで記事を投稿したのに、キャッシュが残っており一覧に表示されない」というケースで有効です。
実装方法は revalidatePath と revalidateTag の2つがあります。
4-1. revalidatePath
主にServer Actionの処理内などで利用します。
指定したURLのキャッシュを丸ごと削除 できます。
'use server';
import { revalidatePath } from 'next/cache';
async function createPost(formData: FormData) {
// 記事を保存する処理...
await db.posts.create({ /* ... */ });
// /posts ページのキャッシュを削除して次回アクセス時に再生成
revalidatePath('/posts'); // 引数はファイルパスではなくURL
}
ポイント
- 引数は ファイルパスではなくURL
- そのページ全体のキャッシュを削除できる
- Server Action と組み合わせるのが定番パターン
4-2. revalidateTag
キャッシュにタグを付ける
Next.js ではキャッシュに保存したデータに対して タグ を付けることができます。
// fetch の場合(自動キャッシュ)
fetch('http://hoge.com', {
next: { tags: ['posts'] }, // ← 取得データに 'posts' というタグを付ける
});
// use cache などの場合
async function getPosts() {
'use cache';
cacheTag('posts'); // ← 取得データに 'posts' というタグを付ける
// 処理の続き...
}
タグ単位でキャッシュを削除
タグを付けたキャッシュを タグ単位で削除 できる機能が revalidateTag です。
import { revalidateTag } from 'next/cache';
revalidateTag('posts', 'max');
第2引数のプロファイル
第2引数には挙動を細かく指定するプロファイルを渡します。
| 値 | 挙動 |
|---|---|
max |
キャッシュを更新するが、以前のキャッシュをすぐに削除せず古いものとして残す |
max を指定すると、新しいキャッシュが用意されるまで古いキャッシュを使い続けるため、ユーザーが待たされない メリットがあります。
revalidatePath vs revalidateTag
| 機能 | 削除単位 | 主な用途 |
|---|---|---|
revalidatePath |
URL(ページ)単位 | 特定ページのキャッシュを丸ごと刷新したい時 |
revalidateTag |
タグ単位 | 複数ページにまたがるデータを一斉に更新したい時 |
5. router.refresh() でブラウザ側のキャッシュを削除する
概要
revalidatePath / revalidateTag は サーバー側 のキャッシュを削除する機能でした。
一方、ブラウザ側のキャッシュ(Router Cache) を削除するのが router.refresh() です。
使い方
'use client';
import { useRouter } from 'next/navigation'; // ⚠ 'next/router' とは別物!!
export default function RefreshButton() {
const router = useRouter();
return (
<button onClick={() => router.refresh()}>
最新化
</button>
);
}
注意点
- インポートは
'next/navigation'(Pages Router 時代の'next/router'ではない) - Server Action を使用している場合は 基本不要 で、サーバー側が自動でキャッシュを削除してくれる
キャッシュ管理の全体像
最後に、各機能の関係を整理しておきます。
┌─ ブラウザ側キャッシュ ──────────────────────────┐
│ Router Cache │
│ ↑ 削除: router.refresh() │
└──────────────────────────────────────────────────┘
↑↓
┌─ サーバー側キャッシュ ──────────────────────────┐
│ Full Route Cache ↑ 削除: revalidatePath │
│ Data Cache ↑ 削除: revalidatePath / │
│ revalidateTag │
│ Request Memorization(1リクエスト内のみ) │
└──────────────────────────────────────────────────┘
↑↓
外部API / DB
実装パターンの定石
| やりたいこと | 使う機能 |
|---|---|
fetch() の結果をリクエスト跨ぎでキャッシュ |
cache: 'force-cache' |
| DB アクセスや重い処理をキャッシュ | 'use cache' |
| 投稿後に一覧ページを最新化(Server Action 内) | revalidatePath('/posts') |
| 複数ページにまたがるデータを一斉更新 | revalidateTag('posts') |
| ブラウザ側のキャッシュをクリア | router.refresh() |
まとめ
Next.js のキャッシュは強力ですが、「自動有効なものを把握する」 + 「明示的な制御が必要なものを覚える」 の2軸で押さえれば怖くありません。
| カテゴリ | 機能 |
|---|---|
| 自動有効 | Full Route Cache / Router Cache / Request Memorization |
| 設定で有効化 | Data Cache(cache: 'force-cache') / 'use cache'
|
| キャッシュの更新 |
revalidatePath / revalidateTag / router.refresh()
|
特に Server Action と revalidatePath の組み合わせは、データ更新後の画面反映を 驚くほど少ないコードで 実現できる強力なパターンです。
キャッシュをマスターして、Next.js の真価を引き出しましょう!