← VercelCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
changed
Update to Vercel
Snapshot Sep 30, 2026 · 23:18 UTC · version 0.21.4
Collection source: not recorded for this historical snapshot. These snapshots do not have a confirmed matching collection source. Differences in file lists alone do not establish changes to the package.
Supporting file metadata differs
Newly listed paths: agents/openai.yaml. This compares saved file lists, not package contents; a different collection source can change the list.
Observed in package metadata. These changes alone do not establish a new customer-facing feature.
Supporting files
Before
[]
After
[{"relative_path":"agents/openai.yaml","size_in_bytes":129}]
Compare saved observations
Download comparison JSONFull technical diff · 1 changed fields
changed /included_files
BEFORE
[]
AFTER
[
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 129
}
]Full snapshot data
{
"name": "next-cache-components",
"description": "Next.js 16 Cache Components guidance — PPR, use cache directive, cacheLife, cacheTag, updateTag, and migration from unstable_cache. Use when implementing partial prerendering, caching strategies, or migrating from older Next.js cache patterns.",
"included_files": [
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 129
}
],
"skill_md_contents": "---\nname: next-cache-components\ndescription: Next.js 16 Cache Components guidance — PPR, use cache directive, cacheLife, cacheTag, updateTag, and migration from unstable_cache. Use when implementing partial prerendering, caching strategies, or migrating from older Next.js cache patterns.\nmetadata:\n priority: 6\n docs:\n - \"https://nextjs.org/docs/app/getting-started/cache-components\"\n - \"https://nextjs.org/docs/app/api-reference/directives/use-cache\"\n pathPatterns:\n - 'next.config.*'\n - 'app/**'\n - 'src/app/**'\n - 'apps/*/app/**'\n - 'apps/*/src/app/**'\n importPatterns:\n - \"next/cache\"\n bashPatterns:\n - '\\bnext\\s+(dev|build)\\b'\n promptSignals:\n phrases:\n - \"use cache\"\n - \"cache components\"\n - \"partial prerendering\"\n - \"PPR\"\n - \"cacheLife\"\n - \"cacheTag\"\n - \"updateTag\"\n - \"unstable_cache\"\n allOf:\n - [cache, component]\n - [cache, directive]\n - [partial, prerender]\n anyOf:\n - \"revalidateTag\"\n - \"stale\"\n - \"revalidate\"\n - \"cache profile\"\n noneOf: []\n minScore: 6\n---\n\n# Cache Components (Next.js 16+)\n\nCache Components enable Partial Prerendering (PPR) - mix static, cached, and dynamic content in a single route.\n\n## Enable Cache Components\n\n```ts\n// next.config.ts\nimport type { NextConfig } from 'next'\n\nconst nextConfig: NextConfig = {\n cacheComponents: true,\n}\n\nexport default nextConfig\n```\n\nThis replaces the old `experimental.ppr` flag.\n\n---\n\n## Three Content Types\n\nWith Cache Components enabled, content falls into three categories:\n\n### 1. Static (Auto-Prerendered)\n\nSynchronous code, imports, pure computations - prerendered at build time:\n\n```tsx\nexport default function Page() {\n return (\n <header>\n <h1>Our Blog</h1> {/* Static - instant */}\n <nav>...</nav>\n </header>\n )\n}\n```\n\n### 2. Cached (`use cache`)\n\nAsync data that doesn't need fresh fetches every request:\n\n```tsx\nasync function BlogPosts() {\n 'use cache'\n cacheLife('hours')\n\n const posts = await db.posts.findMany()\n return <PostList posts={posts} />\n}\n```\n\n### 3. Dynamic (Suspense)\n\nRuntime data that must be fresh - wrap in Suspense:\n\n```tsx\nimport { Suspense } from 'react'\n\nexport default function Page() {\n return (\n <>\n <BlogPosts /> {/* Cached */}\n\n <Suspense fallback={<p>Loading...</p>}>\n <UserPreferences /> {/* Dynamic - streams in */}\n </Suspense>\n </>\n )\n}\n\nasync function UserPreferences() {\n const theme = (await cookies()).get('theme')?.value\n return <p>Theme: {theme}</p>\n}\n```\n\n---\n\n## `use cache` Directive\n\n### File Level\n\n```tsx\n'use cache'\n\nexport default async function Page() {\n // Entire page is cached\n const data = await fetchData()\n return <div>{data}</div>\n}\n```\n\n### Component Level\n\n```tsx\nexport async function CachedComponent() {\n 'use cache'\n const data = await fetchData()\n return <div>{data}</div>\n}\n```\n\n### Function Level\n\n```tsx\nexport async function getData() {\n 'use cache'\n return db.query('SELECT * FROM posts')\n}\n```\n\n---\n\n## Cache Profiles\n\n### Built-in Profiles\n\n```tsx\n'use cache' // Default: 5m stale, 15m revalidate\n```\n\n```tsx\n'use cache: remote' // Platform-provided cache (Redis, KV)\n```\n\n```tsx\n'use cache: private' // For compliance, allows runtime APIs\n```\n\n### `cacheLife()` - Custom Lifetime\n\n```tsx\nimport { cacheLife } from 'next/cache'\n\nasync function getData() {\n 'use cache'\n cacheLife('hours') // Built-in profile\n return fetch('/api/data')\n}\n```\n\nBuilt-in profiles: `'default'`, `'minutes'`, `'hours'`, `'days'`, `'weeks'`, `'max'`\n\n### Inline Configuration\n\n```tsx\nasync function getData() {\n 'use cache'\n cacheLife({\n stale: 3600, // 1 hour - serve stale while revalidating\n revalidate: 7200, // 2 hours - background revalidation interval\n expire: 86400, // 1 day - hard expiration\n })\n return fetch('/api/data')\n}\n```\n\n---\n\n## Cache Invalidation\n\n### `cacheTag()` - Tag Cached Content\n\n```tsx\nimport { cacheTag } from 'next/cache'\n\nasync function getProducts() {\n 'use cache'\n cacheTag('products')\n return db.products.findMany()\n}\n\nasync function getProduct(id: string) {\n 'use cache'\n cacheTag('products', `product-${id}`)\n return db.products.findUnique({ where: { id } })\n}\n```\n\n### `updateTag()` - Immediate Invalidation\n\nUse when you need the cache refreshed within the same request:\n\n```tsx\n'use server'\n\nimport { updateTag } from 'next/cache'\n\nexport async function updateProduct(id: string, data: FormData) {\n await db.products.update({ where: { id }, data })\n updateTag(`product-${id}`) // Immediate - same request sees fresh data\n}\n```\n\n### `revalidateTag()` - Background Revalidation\n\nUse for stale-while-revalidate behavior:\n\n```tsx\n'use server'\n\nimport { revalidateTag } from 'next/cache'\n\nexport async function createPost(data: FormData) {\n await db.posts.create({ data })\n revalidateTag('posts') // Background - next request sees fresh data\n}\n```\n\n---\n\n## Runtime Data Constraint\n\n**Cannot** access `cookies()`, `headers()`, or `searchParams` inside `use cache`.\n\n### Solution: Pass as Arguments\n\n```tsx\n// Wrong - runtime API inside use cache\nasync function CachedProfile() {\n 'use cache'\n const session = (await cookies()).get('session')?.value // Error!\n return <div>{session}</div>\n}\n\n// Correct - extract outside, pass as argument\nasync function ProfilePage() {\n const session = (await cookies()).get('session')?.value\n return <CachedProfile sessionId={session} />\n}\n\nasync function CachedProfile({ sessionId }: { sessionId: string }) {\n 'use cache'\n // sessionId becomes part of cache key automatically\n const data = await fetchUserData(sessionId)\n return <div>{data.name}</div>\n}\n```\n\n### Exception: `use cache: private`\n\nFor compliance requirements when you can't refactor:\n\n```tsx\nasync function getData() {\n 'use cache: private'\n const session = (await cookies()).get('session')?.value // Allowed\n return fetchData(session)\n}\n```\n\n---\n\n## Cache Key Generation\n\nCache keys are automatic based on:\n- **Build ID** - invalidates all caches on deploy\n- **Function ID** - hash of function location\n- **Serializable arguments** - props become part of key\n- **Closure variables** - outer scope values included\n\n```tsx\nasync function Component({ userId }: { userId: string }) {\n const getData = async (filter: string) => {\n 'use cache'\n // Cache key = userId (closure) + filter (argument)\n return fetch(`/api/users/${userId}?filter=${filter}`)\n }\n return getData('active')\n}\n```\n\n---\n\n## Complete Example\n\n```tsx\nimport { Suspense } from 'react'\nimport { cookies } from 'next/headers'\nimport { cacheLife, cacheTag } from 'next/cache'\n\nexport default function DashboardPage() {\n return (\n <>\n {/* Static shell - instant from CDN */}\n <header><h1>Dashboard</h1></header>\n <nav>...</nav>\n\n {/* Cached - fast, revalidates hourly */}\n <Stats />\n\n {/* Dynamic - streams in with fresh data */}\n <Suspense fallback={<NotificationsSkeleton />}>\n <Notifications />\n </Suspense>\n </>\n )\n}\n\nasync function Stats() {\n 'use cache'\n cacheLife('hours')\n cacheTag('dashboard-stats')\n\n const stats = await db.stats.aggregate()\n return <StatsDisplay stats={stats} />\n}\n\nasync function Notifications() {\n const userId = (await cookies()).get('userId')?.value\n const notifications = await db.notifications.findMany({\n where: { userId, read: false }\n })\n return <NotificationList items={notifications} />\n}\n```\n\n---\n\n## Migration from Previous Versions\n\n| Old Config | Replacement |\n|-----------|-------------|\n| `experimental.ppr` | `cacheComponents: true` |\n| `dynamic = 'force-dynamic'` | Remove (default behavior) |\n| `dynamic = 'force-static'` | `'use cache'` + `cacheLife('max')` |\n| `revalidate = N` | `cacheLife({ revalidate: N })` |\n| `unstable_cache()` | `'use cache'` directive |\n\n### Migrating `unstable_cache` to `use cache`\n\n`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:\n\n**Before (`unstable_cache`):**\n\n```tsx\nimport { unstable_cache } from 'next/cache'\n\nconst getCachedUser = unstable_cache(\n async (id) => getUser(id),\n ['my-app-user'],\n {\n tags: ['users'],\n revalidate: 60,\n }\n)\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n const { id } = await params\n const user = await getCachedUser(id)\n return <div>{user.name}</div>\n}\n```\n\n**After (`use cache`):**\n\n```tsx\nimport { cacheLife, cacheTag } from 'next/cache'\n\nasync function getCachedUser(id: string) {\n 'use cache'\n cacheTag('users')\n cacheLife({ revalidate: 60 })\n return getUser(id)\n}\n\nexport default async function Page({ params }: { params: Promise<{ id: string }> }) {\n const { id } = await params\n const user = await getCachedUser(id)\n return <div>{user.name}</div>\n}\n```\n\nKey differences:\n- **No manual cache keys** - `use cache` generates keys automatically from function arguments and closures. The `keyParts` array from `unstable_cache` is no longer needed.\n- **Tags** - Replace `options.tags` with `cacheTag()` calls inside the function.\n- **Revalidation** - Replace `options.revalidate` with `cacheLife({ revalidate: N })` or a built-in profile like `cacheLife('minutes')`.\n- **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.\n\n---\n\n## Limitations\n\n- **Edge runtime not supported** - requires Node.js\n- **Static export not supported** - needs server\n- **Non-deterministic values** (`Math.random()`, `Date.now()`) execute once at build time inside `use cache`\n\nFor request-time randomness outside cache:\n\n```tsx\nimport { connection } from 'next/server'\n\nasync function DynamicContent() {\n await connection() // Defer to request time\n const id = crypto.randomUUID() // Different per request\n return <div>{id}</div>\n}\n```\n\nSources:\n- [Cache Components Guide](https://nextjs.org/docs/app/getting-started/cache-components)\n- [use cache Directive](https://nextjs.org/docs/app/api-reference/directives/use-cache)\n- [unstable_cache (legacy)](https://nextjs.org/docs/app/api-reference/functions/unstable_cache)\n"
}SHA-256: 630cd16c863517ef64d87d3915f6c85737b72fbc81211b23f14ec6d4f1664476