
Next Cache Components
Configure Next.js 16 Cache Components so routes mix static shells, cached data, and streaming dynamic UI without sacrificing TTFB.
Install
npx skills add https://github.com/vercel/nextjs-skills --skill next-cache-componentsWhat is this skill?
- Enables `cacheComponents: true` in `next.config.ts` as the Next.js 16+ replacement for `experimental.ppr`
- Models three content types: static prerender, `'use cache'` with `cacheLife`, and dynamic regions behind Suspense
- Documents `cacheTag`, `updateTag`, and `revalidateTag` for targeted invalidation from Server Actions and Route Handlers
- Shows parameterized caching with `cacheLife` profiles (`hours`, `days`, custom profiles in config)
- Covers migrating from `export const revalidate` toward cache directives and `cacheLife` on Next 16
Adoption & trust: 475 installs on skills.sh; 919 GitHub stars; 3/3 security scanners passed (skills.sh audits).
Recommended Skills
Frontend Designanthropics/skills
Vercel React Best Practicesvercel-labs/agent-skills
Remotion Best Practicesremotion-dev/skills
Vercel Composition Patternsvercel-labs/agent-skills
Develop Userscriptsxixu-me/skills
Next Best Practicesvercel-labs/next-skills
Journey fit
Primary fit
Cache Components and `use cache` are applied while building Next.js pages and server components—the canonical home is frontend app construction. The skill is entirely about App Router pages, directives, and `next.config.ts` flags for rendering behavior.
Common Questions / FAQ
Is Next Cache Components safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Next Cache Components
# 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.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: ```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 f