jpskill.com
🛠️ 開発・MCP コミュニティ 🔴 エンジニア向け 👤 エンジニア・AI開発者

🛠️ Next Cache Components

next-cache-components

Next.js 16のキャッシュコンポーネントを効率的に管理し、キャッシュの有効期間やタグ付け、更新を細かく制御するためのSkill。

⏱ MCPサーバー実装 1日 → 2時間
📜 元の英語説明(参考)

Next.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag

🇯🇵 日本人クリエイター向け解説

一言でいうと

Next.js 16のキャッシュコンポーネントを効率的に管理し、キャッシュの有効期間やタグ付け、更新を細かく制御するためのSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
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
🪟 Windows (PowerShell)
$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. 1. 下の青いボタンを押して next-cache-components.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → next-cache-components フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

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 cache generates keys automatically from function arguments and closures. The keyParts array from unstable_cache is no longer needed.
  • Tags - Replace options.tags with cacheTag() calls inside the function.
  • Revalidation - Replace options.revalidate with cacheLife({ revalidate: N }) or a built-in profile like cacheLife('minutes').
  • Dynamic data - unstable_cache did not support cookies() or headers() inside the callback. The same restriction applies to use 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 inside use 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: