cache-components について
Cache Componentsは、プロジェクトの既存設定を維持しながらNext.jsのキャッシュパターンを適用するスキルです。開発者向けに、バージョンごとの挙動、キャッシュ境界、新鮮度、無効化の確認方法と、本番挙動のテスト方法を示します。
主な機能
- 01Next.jsのバージョン別キャッシュ指針
- 02キャッシュ境界とキーの設計
- 03鮮度と無効化のパターン
- 04Suspenseとリクエストデータの配置
- 05移行とトラブルシューティング
- 06本番挙動のテスト項目
活用シーン
- 01アプリへのCache Components導入
- 02キャッシュ期間と無効化ルールの設定
- 03古い表示やルートのブロックを診断
- 04本番モードでナビゲーションと更新を検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ laguagu/ claude-code-nextjs-skills --skill cache-componentsインストール後、Claude Code で/cache-componentsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
PATTERNS.mdREFERENCE.mdTROUBLESHOOTING.md詳細
キャッシュコンポーネント
移行を求められていない限り、プロジェクトのキャッシュモードを維持してください。これらのパターンを適用する前に、インストールされている Next.js のバージョンと、cacheComponents/partialPrefetching の設定を確認してください。
まず、node_modules/next/dist/docs/ にある関連ガイドを読んでください(16.2 以降は同梱されています。モノレポでは、アプリ固有の next パッケージを使用してください)。Web パスは番号付きフォルダー内のファイルに対応しているため、名前で探してください。たとえば、.../functions/cacheLife は 01-app/03-api-reference/04-functions/cacheLife.md です。01-app/ 以下では、まず 02-guides/migrating-to-cache-components.md、01-getting-started/08-caching.md、02-guides/instant-navigation.md を確認してください。/docs/messages 以下のエラーページは同梱されていません。修正方法は next dev/next build がインラインで表示します。ローカルにドキュメントがない場合は、公式のキャッシュドキュメントを参照してください。正確なエクスポートとシグネチャは node_modules/next/cache.d.ts にも記載されています。16.4 以降の静的出力や段階的な遅延に関する質問では、インストール済みの ensureStatic ルートセグメントリファレンスと、navigation/prefetch 関数のリファレンスを名前で探してください。
目標は、ミューテーション、デプロイ、クライアントナビゲーションの後も鮮度と認可のルールが正しく保たれる、実用的なプリレンダリング済みシェルです。
古い例が誤解させやすい事実
- Next.js 16 では、トップレベルの
cacheComponents: trueでこのモードを有効にします。これはexperimental.dynamicIO、experimental.useCache、experimental.pprを置き換えます。cacheLife/cacheTagはunstable_なしでnext/cacheからインポートします。 16.4 では、partialPrefetchingを明示的に設定してください。未設定のままにすると警告が表示されます。 推奨されるcreate-next-appのデフォルト設定では、両方のフラグが有効になります。 - 有効にすると、セグメントのエクスポート
dynamic、revalidate、fetchCache、dynamicParamsはエラーになり、Edge ランタイムはサポートされません。revalidate = Nはuse cache内でcacheLife(最も近いプロファイルまたはカスタムプロファイル)に置き換えます。force-dynamicとfetchCacheは不要です。fetchのnext: { revalidate, tags }は、use cache関数内のcacheLife/cacheTagに置き換えます。リクエストデータは Suspense の下に置きます。未知のパラメーターに対するdynamicParams = falseはnotFound()に置き換えます。 - 既存の
fetchとunstable_cacheのキャッシュも別のレイヤーとして引き続き機能するため、すべての読み取りを機械的に書き換えないでください。デプロイやインスタンスをまたいでキャッシュが保持されるかどうかは、ストレージが保持されているか、共有されているかによって異なります。セルフホストのインスタンスは、デフォルトではキャッシュを共有しません。use cacheはデフォルトでメモリ内に保存されます。永続化可能なハンドラーを使っても、ビルド/デプロイ ID が変わるとエントリーを再利用できません。 generateStaticParamsは少なくとも 1 つのパラメーターを返す必要があります。[]はビルドに失敗し、エクスポートを削除すると、リクエストのたびにルートがレンダリングされます。revalidate: 0、または 5 分未満のexpireを設定すると、プリレンダリング済みの出力ではなく、リクエスト時に解決される領域になります。プリセットのうち該当するのはsecondsだけです。16.3 ではstaleのしきい値が追加されました(30 秒未満ではプリレンダリングされず、5 分未満では App Shell に含まれません)。- プリレンダリング中に同期的な
new Date()、Math.random()、crypto.randomUUID()を呼び出すとエラーになります。use cache内に取り込むか、Suspense の下でawait io()(16.3 以降はnext/cache)またはawait connection()(next/server)を使って後回しにしてください。 use cache: privateは 16.2 まで実験的機能でした。リクエスト時に実行され、静的シェルには含まれず、カスタムハンドラーは受け付けません。export const instant(16.3。16.2 ではunstable_instant)を使うと、セグメントでインスタントナビゲーションの検証が有効になります。16.3 以降、すべての Page/Default セグメントは、デフォルトのレベルでnext devのみ(開発オーバーレイ)により暗黙的に検証されます。ビルド時の検証には、experimental.instantInsights.validationLevelをexperimental-errorまたはexperimental-manual-errorに設定する必要があります(16.2 のunstable_instantもビルド時に検証されていました)。ナビゲーションのテストには、@next/playwrightのinstant()を使います。instant = falseにするとブロッキングルートを許可しますが、同期 I/O のエラーは解消されません。
アプリ全体への導入やインスタントナビゲーションの作業向けに、Next.js は vercel/next.js/skills でワークフロー用スキル(next-cache-components-adoption、next-cache-components-optimizer)を公開しています。同梱の移行ガイドにも同じ手順が記載されています。
1 つのルートの構成
cacheComponents: true を指定した next 16.3.8 で確認した app/posts/page.tsx(tsc --noEmit、next build、next start でのアクション)。@/lib/* はプロジェクトのコードです。
import { Suspense } from 'react'
import { cookies } from 'next/headers'
import { cacheLife, cacheTag, updateTag } from 'next/cache'
import { insertPost, listPosts } from '@/lib/posts' // project code
import { requireEditor } from '@/lib/auth' // throws unless the session may edit
async function getPosts() {
'use cache' // shared entry: no cookies/headers/searchParams inside
cacheLife('hours')
cacheTag('posts')
return listPosts()
}
async function Theme() {
const theme = (await cookies()).get('theme')?.value // request-time, outside the cache
return <p>Theme: {theme ?? 'system'}</p>
}
async function addPost(formData: FormData) {
'use server'
await requireEditor()
const title = String(formData.get('title') ?? '').trim()
if (!title) return
await insertPost(title)
updateTag('posts') // Server Actions only; the next read waits for fresh data
}
export default async function Page() {
const posts = await getPosts() // cached, so it can be part of the static shell
return (
<>
<ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>
<Suspense fallback={<p>Theme: …</p>}><Theme /></Suspense>
<form action={addPost}><input name="title" /><button>Add</button></form>
</>
)
}
境界を決める
- プロダクトの鮮度要件とデータポリシーが許す場合は、再利用可能な結果をキャッシュします。 静的コンテンツを静的にするだけなら、ディレクティブは不要です。
- 通常の
use cacheスコープ内では、Cookie、ヘッダー、searchParams を読み取ったり、connection()を呼び出したりできません。ヘルパー経由で間接的に行う場合も同様です。これらはスコープの外で読み取り、認可済みのシリアライズ可能な値を渡します。 出力がアカウント/テナント ID に依存する場合、それらの ID をキャッシュキーに含める必要があります。 動的にレンダリングされるルートでは、next-request-in-use-cacheエラーがnext buildを通過し、next startでのみ発生することがあります。 - キャッシュされない I/O とリクエスト専用のコンテンツは、適切な Suspense 境界の下に置きます。Suspense はフォールバック UI を提供しますが、それ自体で同期処理を動的にするわけではありません。
- プライベート/リモートの各バリアントは、インストール済みのドキュメントとデプロイ時のハンドラーに照らして評価してください。プライベートキャッシュはコンプライアンスを保証するものではありません。リモートキャッシュは整合性プロトコルではありません。
鮮度と無効化を定義する
許容できる実際の古さに基づいて有効期間を選びます。名前付きプロファイルはプロジェクト側で上書きできるため、期間を決めつける前に設定を確認してください。
無効化を同じエンティティの複数のルートに反映させる必要がある場合はタグを、ルート固有の出力にはパスの無効化を、要件を満たす場合は有効期限を使用します。変更を加える前に認証、認可、検証を行ってください。影響を受けたキャッシュデータのみを無効化します。
updateTag:即時に期限切れにし、書き込み後の読み取りを実現します。Server Actions 専用です。revalidateTag(tag, 'max'):次回のアクセス時に stale-while-revalidate を行います。revalidateTag(tag, { expire: 0 }):Webhook/Route Handler で即時の期限切れが必要な場合に使用します。引数が1つの形式は非推奨です。
これらのタグ API はタグ付けされた fetch データにも適用できます。これらが利用可能だからといって、Cache Components が有効になっているとは限りません。
問題に応じて参照する資料
- API の境界:キー、シリアライズ、有効期間、ハンドラー、ルート API。
- 構成パターン:テナント分離、ネストされたキャッシュ、パススルー、動的パラメーター。
- トラブルシューティング:ブロッキング、古さ、不整合のある出力を診断します。
本番ビルドを実行してから、next start のもとで、キャッシュがない状態での直接ナビゲーション、クライアントナビゲーション、該当する変更、その後の読み取りを試してください。該当する場合は、テナント分離と複数インスタンス間の無効化も確認します。ビルドが成功しても、こうした動作が保証されるわけではありません。