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 从公开仓库中收录这些内容。

举报或申请下架