コンテンツにスキップ

use cache: private

この機能は現在カナリアチャンネルで利用可能であり、変更される可能性があります。Next.js のアップグレード、およびGitHubでフィードバックを共有することで試すことができます。

'use cache: private' ディレクティブは、Cookie、ヘッダー、または検索パラメータに依存するパーソナライズされたコンテンツの実行時プリフェッチを有効にします。

参考情報: 'use cache: private' は、プリフェッチ可能である必要がありますが、サーバーサイドキャッシュハンドラーには決して保存されないユーザー固有のコンテンツ専用に設計された、use cache のバリアントです。

使用方法

'use cache: private' を使用するには、next.config.ts ファイルで cacheComponents フラグを有効にします。

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  cacheComponents: true,
}
 
export default nextConfig

次に、cacheLife 設定と共に 'use cache: private' を関数に追加し、ページから unstable_prefetch をエクスポートします。

基本的な例

app/product/[id]/page.tsx
import { Suspense } from 'react'
import { cookies } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
 
// REQUIRED: Enable runtime prefetching
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [
    { params: { id: '1' }, cookies: [{ name: 'session-id', value: '1' }] },
  ],
}
 
export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
 
  return (
    <div>
      <ProductDetails id={id} />
      <Suspense fallback={<div>Loading recommendations...</div>}>
        <Recommendations productId={id} />
      </Suspense>
    </div>
  )
}
 
async function Recommendations({ productId }: { productId: string }) {
  const recommendations = await getRecommendations(productId)
 
  return (
    <div>
      {recommendations.map((rec) => (
        <ProductCard key={rec.id} product={rec} />
      ))}
    </div>
  )
}
 
async function getRecommendations(productId: string) {
  'use cache: private'
  cacheTag(`recommendations-${productId}`)
  cacheLife({ stale: 60 }) // Minimum 30 seconds required for runtime prefetch
 
  // Access cookies within private cache functions
  const sessionId = (await cookies()).get('session-id')?.value || 'guest'
 
  return getPersonalizedRecommendations(productId, sessionId)
}

注意: プライベートキャッシュでは、実行時プリフェッチを有効にするために、少なくとも 30 秒の cacheLife stale time が必要です。30 秒未満の値は動的として扱われます。

use cache との違い

通常の use cache は、サーバーでキャッシュできる静的で共有コンテンツ向けに設計されていますが、'use cache: private' は、動的でユーザー固有のコンテンツ向けに特別に設計されており、

  1. パーソナライズされた - Cookie、ヘッダー、または検索パラメータによって異なります
  2. プリフェッチ可能 - ユーザーがページに移動する前にロードできます
  3. クライアントのみ - サーバーサイドキャッシュハンドラーに永続化されることはありません
機能use cache'use cache: private'
await cookies() へのアクセスいいえはい
await headers() へのアクセスいいえはい
await searchParams へのアクセスいいえはい
キャッシュハンドラーに格納はい (サーバーサイド)いいえ (クライアントサイドのみ)
実行時プリフェッチN/A (すでに静的)はい (設定時)
キャッシュスコープグローバル (共有)ユーザーごと (分離)
ユースケース静的、共有コンテンツパーソナライズされた、ユーザー固有のコンテンツ

仕組み

実行時プリフェッチ

ユーザーが unstable_prefetch = { mode: 'runtime' } のリンクにカーソルを合わせたり、表示したりすると

  1. 静的コンテンツ はすぐにプリフェッチされます (レイアウト、ページシェル)
  2. プライベートキャッシュ関数 は、ユーザーの現在の Cookie/ヘッダーで実行されます
  3. 結果は クライアントサイドの Resume Data Cache に保存されます
  4. ナビゲーションは瞬時です - 静的コンテンツとパーソナライズされたコンテンツの両方がすでにロードされています

参考情報: 'use cache: private' がないと、パーソナライズされたコンテンツはプリフェッチできず、ナビゲーションが完了するまで待つ必要があります。実行時プリフェッチは、ユーザーの現在のリクエストコンテキストでキャッシュ関数を実行することで、この遅延を解消します。

ストレージの挙動

プライベートキャッシュは、サーバーサイドキャッシュハンドラー (Redis、Vercel Data Cache など) には決して永続化されません。それらは、

  1. パーソナライズされたコンテンツの実行時プリフェッチを有効にする
  2. セッション中にプリフェッチされたデータをクライアントサイドキャッシュに保存する
  3. タグと stale time でキャッシュの無効化を調整する

これにより、ユーザー固有のデータがユーザー間で誤って共有されることを防ぎつつ、高速なプリフェッチナビゲーションを可能にします。

Stale time の要件

注意: cacheLife stale time が 30 秒未満の関数は、'use cache: private' を使用している場合でも、実行時プリフェッチされません。これは、ナビゲーション時に stale になる可能性が高い、急速に変化するデータのプリフェッチを防ぐためです。

// Will be runtime prefetched (stale ≥ 30s)
cacheLife({ stale: 60 })
 
// Will be runtime prefetched (stale ≥ 30s)
cacheLife({ stale: 30 })
 
// Will NOT be runtime prefetched (stale < 30s)
cacheLife({ stale: 10 })

プライベートキャッシュで許可されるリクエスト API

以下のリクエスト固有の API は、'use cache: private' 関数内で使用できます。

APIuse cache で許可'use cache: private' で許可
cookies()いいえはい
headers()いいえはい
searchParamsいいえはい
connection()いいえいいえ

注意: connection() API は、安全にキャッシュできない接続固有の情報を提供するため、use cache'use cache: private' の両方で禁止されています。

ネストのルール

プライベートキャッシュには、ユーザー固有のデータが共有キャッシュに漏洩するのを防ぐための特定のネストルールがあります。

  • プライベートキャッシュは、他のプライベートキャッシュの中にネストできます
  • プライベートキャッシュは、パブリックキャッシュ ('use cache', 'use cache: remote') の中にネストできません
  • パブリックキャッシュは、プライベートキャッシュの中にネストできます
// VALID: Private inside private
async function outerPrivate() {
  'use cache: private'
  const result = await innerPrivate()
  return result
}
 
async function innerPrivate() {
  'use cache: private'
  return getData()
}
 
// INVALID: Private inside public
async function outerPublic() {
  'use cache'
  const result = await innerPrivate() // Error!
  return result
}
 
async function innerPrivate() {
  'use cache: private'
  return getData()
}

パーソナライズされた商品レコメンデーション

この例では、ユーザーのセッション Cookie に基づいてパーソナライズされた商品レコメンデーションをキャッシュする方法を示します。レコメンデーションは、ユーザーが商品リンクにカーソルを合わせたときに実行時にプリフェッチされます。

app/product/[id]/page.tsx
import { cookies } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [
    { params: { id: '1' }, cookies: [{ name: 'user-id', value: 'user-123' }] },
  ],
}
 
async function getRecommendations(productId: string) {
  'use cache: private'
  cacheTag(`recommendations-${productId}`)
  cacheLife({ stale: 60 })
 
  const userId = (await cookies()).get('user-id')?.value
 
  // Fetch personalized recommendations based on user's browsing history
  const recommendations = await db.recommendations.findMany({
    where: { userId, productId },
  })
 
  return recommendations
}

ユーザー固有の価格設定

ユーザーのティアによって異なる価格情報をキャッシュし、パーソナライズされたレートがすでにロードされた価格ページへの即時ナビゲーションを可能にします。

app/pricing/page.tsx
import { cookies } from 'next/headers'
import { cacheLife } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [{ cookies: [{ name: 'user-tier', value: 'premium' }] }],
}
 
async function getPricing() {
  'use cache: private'
  cacheLife({ stale: 300 }) // 5 minutes
 
  const tier = (await cookies()).get('user-tier')?.value || 'free'
 
  // Return tier-specific pricing
  return db.pricing.findMany({ where: { tier } })
}

ヘッダーに基づくローカライズされたコンテンツ

ユーザーの Accept-Language ヘッダーに基づいてローカライズされたコンテンツを提供し、ナビゲーション前に正しい言語バリアントをプリフェッチします。

app/page.tsx
import { headers } from 'next/headers'
import { cacheLife, cacheTag } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [{ headers: [{ name: 'accept-language', value: 'en-US' }] }],
}
 
async function getLocalizedContent() {
  'use cache: private'
  cacheTag('content')
  cacheLife({ stale: 3600 }) // 1 hour
 
  const headersList = await headers()
  const locale = headersList.get('accept-language')?.split(',')[0] || 'en-US'
 
  return db.content.findMany({ where: { locale } })
}

ユーザー設定に基づいた検索結果

ユーザー固有の設定を含む検索結果をプリフェッチし、パーソナライズされた検索結果が瞬時にロードされるようにします。

app/search/page.tsx
import { cookies } from 'next/headers'
import { cacheLife } from 'next/cache'
 
export const unstable_prefetch = {
  mode: 'runtime',
  samples: [
    {
      searchParams: { q: 'laptop' },
      cookies: [{ name: 'preferences', value: 'compact-view' }],
    },
  ],
}
 
async function getSearchResults(query: string) {
  'use cache: private'
  cacheLife({ stale: 120 }) // 2 minutes
 
  const preferences = (await cookies()).get('preferences')?.value
 
  // Apply user preferences to search results
  return searchWithPreferences(query, preferences)
}

知っておくと良いこと:

  • プライベートキャッシュは一時的であり、セッション期間中はクライアントサイドキャッシュにのみ存在します。
  • プライベートキャッシュの結果は、サーバーサイドキャッシュハンドラーに書き込まれることはありません。
  • 実行時プリフェッチを機能させるには、unstable_prefetch エクスポートが必要です。
  • プライベートキャッシュをプリフェッチするには、最低 30 秒の stale time が必要です。
  • cacheTag() および revalidateTag() を使用して、プライベートキャッシュを無効化できます。
  • 各ユーザーは、Cookie/ヘッダーに基づいて独自のプライベートキャッシュエントリを取得します。

プラットフォームのサポート

デプロイメントオプションサポート
Node.jsサーバーはい
Dockerコンテナはい
静的エクスポートいいえ
アダプターはい

バージョン履歴

バージョン変更履歴
v16.0.0'use cache: private' が実験的機能として導入されました。