Next.js 16 になって、キャッシュの考え方が大きく変わりました。これまでの「fetch がデフォルトでキャッシュされる」モデルから、「明示的に use cache と書いた箇所だけキャッシュする」モデルへの転換です。
この変化は、業務システムを受託で作っている私たちのような開発会社にとっては、むしろ歓迎すべき変更だと感じています。何がキャッシュされていて、何が毎回サーバーで実行されているのか、コードを見ただけで判断できるようになるからです。
一方で、中小企業向けのフルスクラッチで「どこに use cache を入れるべきか」「cacheLife と cacheTag をどう使い分けるか」は、Next.js 公式ドキュメントを読んだだけでは判断しづらい部分でもあります。本記事では、私たちが社内・受託案件で運用している判断基準をまとめます。
Next.js 16 のキャッシュは「明示的オプトイン」になった
Next.js 16 では、ページ・レイアウト・API ルートのコードはすべて、デフォルトでリクエスト時に実行されます(Next.js 16 公式ブログ)。何かを「キャッシュしたい」と思った場合は、関数やコンポーネントの先頭に "use cache" を書いて、明示的にキャッシュ対象であることを宣言します。
// app/lib/products.ts
export async function getFeaturedProducts() {
"use cache";
const products = await db.product.findMany({
where: { featured: true },
orderBy: { updatedAt: "desc" },
});
return products;
}
このシンプルさが効いてきます。コードレビューで「ここはキャッシュされていますか?」という議論が、関数の先頭を見るだけで終わるようになりました。
どこに use cache を入れるか、3つの判断軸
私たちは、業務システムの設計時に次の3つの軸でキャッシュ対象を選びます。
軸1: 全ユーザー共通か、ユーザー個別か
全ユーザーで同じ結果を見せてよい関数は use cache の候補です。たとえば「公開中の商品一覧」「企業の問い合わせフォームに表示する選択肢マスタ」「公開記事のリスト」など。
逆にログインユーザーごとに結果が変わるもの、たとえば「自分の発注履歴」「自分の権限で見える顧客一覧」は、原則キャッシュしません。どうしてもキャッシュしたい場合は、後述の use cache: private を検討します。
軸2: 更新頻度がどのくらいか
1日に数回しか更新されないデータと、1分ごとに変わるデータでは、キャッシュ戦略がまったく違います。
- 商品マスタ・カテゴリ一覧 → 1日数回 →
cacheLife("hours")
- 在庫数・受注ステータス → 数分単位 →
cacheLife("minutes") or 都度更新
- ダッシュボードの集計値 → 1日1回バッチ更新 →
cacheLife("days")
軸3: 古いデータを見せてよいか
ECの商品説明であれば、数分古いキャッシュを見せても大きな問題にはなりません。一方、銀行の残高や予約システムの空き枠は、1秒でも古いデータを見せられないケースがあります。
「どれくらい古いデータならOKか」を業務側にヒアリングして決めるのが、いちばん確実です。

cacheLife と cacheTag の使い分け
use cache と組み合わせて使う関数が2つあります。cacheLife と cacheTag です。
cacheLife: 時間ベースの有効期限
cacheLife はキャッシュの寿命を決めるための関数です。Next.js が用意した "minutes" "hours" "days" "weeks" などのプロファイルを使うか、独自のプロファイルを定義できます。
import { cacheLife } from "next/cache";
export async function getCategoryList() {
"use cache";
cacheLife("hours");
return await db.category.findMany({ where: { active: true } });
}
中小企業の業務システムでは、まずプリセットの "minutes" "hours" で十分なことが多いです。next.config.js で独自プロファイルを定義する必要が出てくるのは、独特の更新頻度を持つデータ(毎週月曜の朝に更新される、など)に当たったときです。
cacheTag: 業務イベント起点で無効化する
cacheTag は、キャッシュにタグを付けて、後から revalidateTag で明示的に無効化できる仕組みです。
import { cacheTag } from "next/cache";
export async function getProductDetail(productId: string) {
"use cache";
cacheTag(`product-${productId}`, "products");
return await db.product.findUnique({ where: { id: productId } });
}
商品マスタを更新した瞬間に、関連する全キャッシュを破棄したい場合は、Server Action からタグ単位で破棄します。
"use server";
import { revalidateTag } from "next/cache";
export async function updateProduct(productId: string, data: ProductInput) {
await db.product.update({ where: { id: productId }, data });
// この商品だけ破棄
revalidateTag(`product-${productId}`);
// 一覧系も破棄
revalidateTag("products");
}
私たちの実務感覚では、cacheLife だけで足りるケースは少なく、ほとんどの業務データには cacheTag も併用します。「業務側のアクションでデータが更新された瞬間に、即座にUIに反映したい」というニーズが必ずあるからです。
ハマりやすいポイントとして、use cache で囲んだ関数の中では cookies() headers() searchParams といったリクエスト時のAPIを直接呼べません(Next.js 公式 use cache ドキュメント)。
これらの値はキャッシュ関数の外で取得し、引数として渡す設計に変えます。
// ✗ NG: use cache 内で cookies を呼ぼうとしている
export async function getMyOrders() {
"use cache";
const cookieStore = await cookies();
const userId = cookieStore.get("userId")?.value;
return await db.order.findMany({ where: { userId } });
}
// ○ OK: 引数として userId を受け取る
async function getOrdersByUser(userId: string) {
"use cache";
cacheTag(`orders-${userId}`);
return await db.order.findMany({ where: { userId } });
}
// 呼び出し側(Server Component)で cookies を読んで渡す
export async function MyOrdersPage() {
const cookieStore = await cookies();
const userId = cookieStore.get("userId")?.value;
if (!userId) redirect("/login");
const orders = await getOrdersByUser(userId);
return <OrderList orders={orders} />;
}
「ユーザー個別データはキャッシュしない」という大原則と組み合わせると、use cache を入れる位置に迷うことがそもそも減ります。
中小企業向けの現実的な初期設計
新規プロジェクトを立ち上げるとき、私たちは次の方針でスタートしています。
- デフォルトはキャッシュなし。
use cache を書いた関数だけがキャッシュ対象
- マスタ系(商品・カテゴリ・顧客区分など)は
cacheLife("hours") + cacheTag
- ダッシュボードの集計値は
cacheLife("days") + 夜間バッチで revalidateTag
- 個人別データは原則キャッシュしない。必要なら呼び出し側で memoize
- 本番リリース後、Vercel のメトリクスを見て、キャッシュヒット率の低い関数から見直す
最初から完璧なキャッシュ設計を目指すと、設計コストが膨らみます。中小企業の予算感では「まずキャッシュなしで動くものを作って、ボトルネックが見えてから足す」アプローチのほうが合っていることが多いです。
SaaS から移行するときに効いてくる
WordPress や既存 SaaS から自社専用システムに切り替える案件で、Next.js 16 のキャッシュ設計の良さを実感する場面があります。SaaS では「裏側のキャッシュ挙動が見えない」「無効化のタイミングを制御できない」ことが運用上のストレスになっていることが多いのですが、フルスクラッチで作れば、業務イベント起点で正確に無効化する仕組みを自分たちで握れます。
このあたりの判断軸については、受発注管理を SaaS から Next.js フルスクラッチに切り替える判断基準、中小企業の「SaaS疲れ」を解消する|Next.js フルスクラッチという選択肢も併せて参考になると思います。社内ダッシュボード設計の観点は散らばった社内データを1枚のダッシュボードに集約する|Next.js × Firebaseの設計、AI連携を組み合わせる場合は【2026年版】Next.js × AIエージェントで業務を自動化する実務ガイドが参考になります。
まとめ
Next.js 16 の use cache は、中小企業向けの業務システムでも十分に活用できる設計に進化しています。「キャッシュ対象を明示する」「cacheLife で寿命、cacheTag で業務イベント無効化」「個人別データはキャッシュしない」という3つを押さえれば、運用しやすいキャッシュ設計に近づきます。
設計時のチェックリスト(PDF版)や、移行コストの見積もり方法については個別にお渡ししています。具体の現場に当てはめて議論したい方は、15分の壁打ちからご相談を受け付けています。
キャッシュ設計とあわせて、Next.js 開発そのもののパフォーマンスを底上げしたい方には、高速レンダリングを極めるNext.js専門開発チームのパフォーマンスチューニング術、【2026年版】Next.js 16の新機能とパフォーマンス最適化、Next.jsシステム開発におけるパフォーマンス最適化の実践的アプローチもあわせてご覧ください。use cache だけでなく、レンダリング全体の最適化まで含めて中小企業のフルスクラッチ案件に落とし込む視点が整理できるはずです。
本記事は Next.js 16.x 時点の情報です
最終更新:2026年5月6日