🛠️ Next Cache Components
Next.js 16のキャッシュコンポーネントを効率的に管理し、キャッシュの有効期間やタグ付け、更新を細かく制御するためのSkill。
📜 元の英語説明(参考)
Next.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag
🇯🇵 日本人クリエイター向け解説
Next.js 16のキャッシュコンポーネントを効率的に管理し、キャッシュの有効期間やタグ付け、更新を細かく制御するためのSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o next-cache-components.zip https://jpskill.com/download/5101.zip && unzip -o next-cache-components.zip && rm next-cache-components.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/5101.zip -OutFile "$d\next-cache-components.zip"; Expand-Archive "$d\next-cache-components.zip" -DestinationPath $d -Force; ri "$d\next-cache-components.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
next-cache-components.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
next-cache-componentsフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-17
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
💬 こう話しかけるだけ — サンプルプロンプト
- › Next Cache Components を使って、最小構成のサンプルコードを示して
- › Next Cache Components の主な使い方と注意点を教えて
- › Next Cache Components を既存プロジェクトに組み込む方法を教えて
これをClaude Code に貼るだけで、このSkillが自動発動します。
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
[Skill 名] next-cache-components
キャッシュコンポーネント (Next.js 16+)
キャッシュコンポーネントは、Partial Prerendering (PPR) を可能にします。これにより、静的、キャッシュ済み、動的なコンテンツを単一のルートで混在させることができます。
キャッシュコンポーネントを有効にする
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
これは、古い experimental.ppr フラグに代わるものです。
3つのコンテンツタイプ
キャッシュコンポーネントを有効にすると、コンテンツは次の3つのカテゴリに分類されます。
1. 静的 (自動プリレンダリング)
同期コード、インポート、純粋な計算は、ビルド時にプリレンダリングされます。
export default function Page() {
return (
<header>
<h1>Our Blog</h1> {/* 静的 - 即時 */}
<nav>...</nav>
</header>
)
}
2. キャッシュ済み (use cache)
リクエストごとに新しいフェッチを必要としない非同期データです。
async function BlogPosts() {
'use cache'
cacheLife('hours')
const posts = await db.posts.findMany()
return <PostList posts={posts} />
}
3. 動的 (Suspense)
最新である必要があるランタイムデータは、Suspense でラップします。
import { Suspense } from 'react'
export default function Page() {
return (
<>
<BlogPosts /> {/* キャッシュ済み */}
<Suspense fallback={<p>Loading...</p>}>
<UserPreferences /> {/* 動的 - ストリームで読み込まれる */}
</Suspense>
</>
)
}
async function UserPreferences() {
const theme = (await cookies()).get('theme')?.value
return <p>Theme: {theme}</p>
}
use cache ディレクティブ
ファイルレベル
'use cache'
export default async function Page() {
// ページ全体がキャッシュされる
const data = await fetchData()
return <div>{data}</div>
}
コンポーネントレベル
export async function CachedComponent() {
'use cache'
const data = await fetchData()
return <div>{data}</div>
}
関数レベル
export async function getData() {
'use cache'
return db.query('SELECT * FROM posts')
}
キャッシュプロファイル
組み込みプロファイル
'use cache' // デフォルト: 5分で陳腐化、15分で再検証
'use cache: remote' // プラットフォーム提供のキャッシュ (Redis, KV)
'use cache: private' // コンプライアンス用、ランタイムAPIを許可
cacheLife() - カスタム有効期間
import { cacheLife } from 'next/cache'
async function getData() {
'use cache'
cacheLife('hours') // 組み込みプロファイル
return fetch('/api/data')
}
組み込みプロファイル: 'default', 'minutes', 'hours', 'days', 'weeks', 'max'
インライン設定
async function getData() {
'use cache'
cacheLife({
stale: 3600, // 1時間 - 再検証中に古いデータを配信
revalidate: 7200, // 2時間 - バックグラウンド再検証間隔
expire: 86400, // 1日 - ハードな有効期限
})
return fetch('/api/data')
}
キャッシュの無効化
cacheTag() - キャッシュされたコンテンツにタグ付け
import { cacheTag } from 'next/cache'
async function getProducts() {
'use cache'
cacheTag('products')
return db.products.findMany()
}
async function getProduct(id: string) {
'use cache'
cacheTag('products', `product-${id}`)
return db.products.findUnique({ where: { id } })
}
updateTag() - 即時無効化
同じリクエスト内でキャッシュを更新する必要がある場合に使用します。
'use server'
import { updateTag } from 'next/cache'
export async function updateProduct(id: string, data: FormData) {
await db.products.update({ where: { id }, data })
updateTag(`product-${id}`) // 即時 - 同じリクエストで新しいデータが見える
}
revalidateTag() - バックグラウンド再検証
stale-while-revalidate の動作に使用します。
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost(data: FormData) {
await db.posts.create({ data })
revalidateTag('posts') // バックグラウンド - 次のリクエストで新しいデータが見える
}
ランタイムデータの制約
use cache の内部では cookies()、headers()、searchParams にアクセスできません。
解決策: 引数として渡す
// 誤り - use cache の内部でランタイムAPIを使用
async function CachedProfile() {
'use cache'
const session = (await cookies()).get('session')?.value // エラー!
return <div>{session}</div>
}
// 正しい - 外部で抽出し、引数として渡す
async function ProfilePage() {
const session = (await cookies()).get('session')?.value
return <CachedProfile sessionId={session} />
}
async function CachedProfile({ sessionId }: { sessionId: string }) {
'use cache'
// sessionId は自動的にキャッシュキーの一部になる
const data = await fetchUserData(sessionId)
return <div>{data.name}</div>
}
例外: use cache: private
リファクタリングできないコンプライアンス要件がある場合:
async function getData() {
'use cache: private'
const session = (await cookies()).get('session')?.value // 許可される
return fetchData(session)
}
キャッシュキーの生成
キャッシュキーは以下に基づいて自動的に生成されます。
- ビルドID - デプロイ時にすべてのキャッシュを無効化します
- 関数ID - 関数の場所のハッシュ
- シリアライズ可能な引数 - props がキーの一部になります
- クロージャ変数 - 外側のスコープの値が含まれます
async function Component({ userId }: { userId: string }) {
const getData = async (filter: string) => {
'use cache'
// キャッシュキー = userId (クロージャ) + filter (引数)
return fetch(`/api/users/${userId}?filter=${filter}`)
}
return getData('active')
}
完全な例
import { Suspense } from 'react'
import { cookies } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
export default function DashboardPage() {
return (
<>
{/* 静的シェル - CDNから即時 */}
<header><h1>Dashboard</h1></header>
<nav>...</nav>
{/* キャッシュ済み - 高速、1時間ごとに再検証 */}
<Stats />
{/* 動的 - 新しいデータでストリームで読み込まれる */}
<Suspense fallback={<NotificationsSkeleton />}>
<Notifications />
</Suspense>
</>
)
}
async f 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Cache Components (Next.js 16+)
Cache Components enable Partial Prerendering (PPR) - mix static, cached, and dynamic content in a single route.
Enable Cache Components
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
This replaces the old experimental.ppr flag.
Three Content Types
With Cache Components enabled, content falls into three categories:
1. Static (Auto-Prerendered)
Synchronous code, imports, pure computations - prerendered at build time:
export default function Page() {
return (
<header>
<h1>Our Blog</h1> {/* Static - instant */}
<nav>...</nav>
</header>
)
}
2. Cached (use cache)
Async data that doesn't need fresh fetches every request:
async function BlogPosts() {
'use cache'
cacheLife('hours')
const posts = await db.posts.findMany()
return <PostList posts={posts} />
}
3. Dynamic (Suspense)
Runtime data that must be fresh - wrap in Suspense:
import { Suspense } from 'react'
export default function Page() {
return (
<>
<BlogPosts /> {/* Cached */}
<Suspense fallback={<p>Loading...</p>}>
<UserPreferences /> {/* Dynamic - streams in */}
</Suspense>
</>
)
}
async function UserPreferences() {
const theme = (await cookies()).get('theme')?.value
return <p>Theme: {theme}</p>
}
use cache Directive
File Level
'use cache'
export default async function Page() {
// Entire page is cached
const data = await fetchData()
return <div>{data}</div>
}
Component Level
export async function CachedComponent() {
'use cache'
const data = await fetchData()
return <div>{data}</div>
}
Function Level
export async function getData() {
'use cache'
return db.query('SELECT * FROM posts')
}
Cache Profiles
Built-in Profiles
'use cache' // Default: 5m stale, 15m revalidate
'use cache: remote' // Platform-provided cache (Redis, KV)
'use cache: private' // For compliance, allows runtime APIs
cacheLife() - Custom Lifetime
import { cacheLife } from 'next/cache'
async function getData() {
'use cache'
cacheLife('hours') // Built-in profile
return fetch('/api/data')
}
Built-in profiles: 'default', 'minutes', 'hours', 'days', 'weeks', 'max'
Inline Configuration
async function getData() {
'use cache'
cacheLife({
stale: 3600, // 1 hour - serve stale while revalidating
revalidate: 7200, // 2 hours - background revalidation interval
expire: 86400, // 1 day - hard expiration
})
return fetch('/api/data')
}
Cache Invalidation
cacheTag() - Tag Cached Content
import { cacheTag } from 'next/cache'
async function getProducts() {
'use cache'
cacheTag('products')
return db.products.findMany()
}
async function getProduct(id: string) {
'use cache'
cacheTag('products', `product-${id}`)
return db.products.findUnique({ where: { id } })
}
updateTag() - Immediate Invalidation
Use when you need the cache refreshed within the same request:
'use server'
import { updateTag } from 'next/cache'
export async function updateProduct(id: string, data: FormData) {
await db.products.update({ where: { id }, data })
updateTag(`product-${id}`) // Immediate - same request sees fresh data
}
revalidateTag() - Background Revalidation
Use for stale-while-revalidate behavior:
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost(data: FormData) {
await db.posts.create({ data })
revalidateTag('posts') // Background - next request sees fresh data
}
Runtime Data Constraint
Cannot access cookies(), headers(), or searchParams inside use cache.
Solution: Pass as Arguments
// Wrong - runtime API inside use cache
async function CachedProfile() {
'use cache'
const session = (await cookies()).get('session')?.value // Error!
return <div>{session}</div>
}
// Correct - extract outside, pass as argument
async function ProfilePage() {
const session = (await cookies()).get('session')?.value
return <CachedProfile sessionId={session} />
}
async function CachedProfile({ sessionId }: { sessionId: string }) {
'use cache'
// sessionId becomes part of cache key automatically
const data = await fetchUserData(sessionId)
return <div>{data.name}</div>
}
Exception: use cache: private
For compliance requirements when you can't refactor:
async function getData() {
'use cache: private'
const session = (await cookies()).get('session')?.value // Allowed
return fetchData(session)
}
Cache Key Generation
Cache keys are automatic based on:
- Build ID - invalidates all caches on deploy
- Function ID - hash of function location
- Serializable arguments - props become part of key
- Closure variables - outer scope values included
async function Component({ userId }: { userId: string }) {
const getData = async (filter: string) => {
'use cache'
// Cache key = userId (closure) + filter (argument)
return fetch(`/api/users/${userId}?filter=${filter}`)
}
return getData('active')
}
Complete Example
import { Suspense } from 'react'
import { cookies } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
export default function DashboardPage() {
return (
<>
{/* Static shell - instant from CDN */}
<header><h1>Dashboard</h1></header>
<nav>...</nav>
{/* Cached - fast, revalidates hourly */}
<Stats />
{/* Dynamic - streams in with fresh data */}
<Suspense fallback={<NotificationsSkeleton />}>
<Notifications />
</Suspense>
</>
)
}
async function Stats() {
'use cache'
cacheLife('hours')
cacheTag('dashboard-stats')
const stats = await db.stats.aggregate()
return <StatsDisplay stats={stats} />
}
async function Notifications() {
const userId = (await cookies()).get('userId')?.value
const notifications = await db.notifications.findMany({
where: { userId, read: false }
})
return <NotificationList items={notifications} />
}
Migration from Previous Versions
| Old Config | Replacement |
|---|---|
experimental.ppr |
cacheComponents: true |
dynamic = 'force-dynamic' |
Remove (default behavior) |
dynamic = 'force-static' |
'use cache' + cacheLife('max') |
revalidate = N |
cacheLife({ revalidate: N }) |
unstable_cache() |
'use cache' directive |
Migrating unstable_cache to use cache
unstable_cache has been replaced by the use cache directive in Next.js 16. When cacheComponents is enabled, convert unstable_cache calls to use cache functions:
Before (unstable_cache):
import { unstable_cache } from 'next/cache'
const getCachedUser = unstable_cache(
async (id) => getUser(id),
['my-app-user'],
{
tags: ['users'],
revalidate: 60,
}
)
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const user = await getCachedUser(id)
return <div>{user.name}</div>
}
After (use cache):
import { cacheLife, cacheTag } from 'next/cache'
async function getCachedUser(id: string) {
'use cache'
cacheTag('users')
cacheLife({ revalidate: 60 })
return getUser(id)
}
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const user = await getCachedUser(id)
return <div>{user.name}</div>
}
Key differences:
- No manual cache keys -
use cachegenerates keys automatically from function arguments and closures. ThekeyPartsarray fromunstable_cacheis no longer needed. - Tags - Replace
options.tagswithcacheTag()calls inside the function. - Revalidation - Replace
options.revalidatewithcacheLife({ revalidate: N })or a built-in profile likecacheLife('minutes'). - Dynamic data -
unstable_cachedid not supportcookies()orheaders()inside the callback. The same restriction applies touse cache, but you can use'use cache: private'if needed.
Limitations
- Edge runtime not supported - requires Node.js
- Static export not supported - needs server
- Non-deterministic values (
Math.random(),Date.now()) execute once at build time insideuse cache
For request-time randomness outside cache:
import { connection } from 'next/server'
async function DynamicContent() {
await connection() // Defer to request time
const id = crypto.randomUUID() // Different per request
return <div>{id}</div>
}
Sources: