Next Cache Components

作者 vercel-labs672deb21e70e無授權條款1.6K 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫7 個月前更新

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

精選僅含說明Software Development
AI 產生的概覽

Next.js 16 快取元件參考指南:PPR、use cache 指令、cacheLife、cacheTag 與移轉。

功能
此技能是一份參考文件,說明 Next.js 16 的快取元件(Cache Components)與部分預先算繪(PPR)。內容涵蓋啟用 cacheComponents 設定、三種內容類型(靜態、快取、動態)、檔案/元件/函式層級的 use cache 指令、快取設定檔與 cacheLife 設定、cacheTag、updateTag 與 revalidateTag 失效機制、執行階段資料限制、快取鍵產生、完整的儀表板範例,以及從 unstable_cache 等舊 API 的移轉。它產出的是說明性指引與程式碼片段,而非檔案或指令碼。
適用情境
適用於開發需要快取或部分預先算繪行為的 Next.js 16 應用程式時。適合解答關於 use cache 指令、快取時效、以標籤為基礎的失效,或從 experimental.ppr 與 unstable_cache 移轉的問題。
執行需求
此技能不附帶指令碼或資源,僅包含說明與程式碼範例。套用其中的指引需要執行於 Node.js 上的 Next.js 16 專案,因為所描述的功能不支援 Edge 執行階段與靜態匯出。

Cache Components (Next.js 16+)

Cache Components enable Partial Prerendering (PPR) - mix static, cached, and dynamic content in a single route.

Enable Cache Components

ts
// next.config.tsimport 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:

tsx
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:

tsx
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:

tsx
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

tsx
'use cache'
export default async function Page() {  // Entire page is cached  const data = await fetchData()  return <div>{data}</div>}

Component Level

tsx
export async function CachedComponent() {  'use cache'  const data = await fetchData()  return <div>{data}</div>}

Function Level

tsx
export async function getData() {  'use cache'  return db.query('SELECT * FROM posts')}

Cache Profiles

Built-in Profiles

tsx
'use cache'                    // Default: 5m stale, 15m revalidate
tsx
'use cache: remote'           // Platform-provided cache (Redis, KV)
tsx
'use cache: private'          // For compliance, allows runtime APIs

cacheLife() - Custom Lifetime

tsx
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

tsx
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

tsx
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:

tsx
'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:

tsx
'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

tsx
// Wrong - runtime API inside use cacheasync function CachedProfile() {  'use cache'  const session = (await cookies()).get('session')?.value  // Error!  return <div>{session}</div>}
// Correct - extract outside, pass as argumentasync 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:

tsx
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
tsx
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

tsx
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 ConfigReplacement
experimental.pprcacheComponents: true
dynamic = 'force-dynamic'Remove (default behavior)
dynamic = 'force-static''use cache' + cacheLife('max')
revalidate = NcacheLife({ 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):

tsx
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):

tsx
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:

tsx
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:

來源與署名

來源:vercel-labs/openreview位於.agents/skills/next-cache-components提交672deb2

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架