
Next Cache Components
Enable Next.js 16 Cache Components and Partial Prerendering so routes mix static shell, cached data, and dynamic Suspense in one page.
Overview
next-cache-components is an agent skill most often used in Build (also Ship perf) that configures Next.js 16 Cache Components, PPR, use cache, cacheLife, cacheTag, and updateTag.
Install
npx skills add https://github.com/vercel-labs/openreview --skill next-cache-componentsWhat is this skill?
- Enables cacheComponents in next.config.ts replacing experimental.ppr
- Documents three content types: static prerender, cached use cache segments, and dynamic Suspense
- Covers cacheLife profiles, cacheTag, and updateTag for invalidation
- Shows mixing BlogPosts cached blocks with UserPreferences streamed inside Suspense
- Three content types: static auto-prerendered, cached use cache, and dynamic Suspense
Adoption & trust: 36 installs on skills.sh; 1.4k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your Next.js route is either fully dynamic and slow or fully static and stale, and you cannot mix cached and fresh segments cleanly.
Who is it for?
Indie builders on Next.js 16+ who need marketing-speed shells with cached DB reads and fresh per-user corners on the same page.
Skip if: Legacy Next versions without Cache Components, or simple sites with no server data where static export is enough.
When should I use this skill?
Working with Next.js 16 Cache Components, PPR, use cache directive, cacheLife, cacheTag, or updateTag.
What do I get? / Deliverables
You enable PPR-style routes with static headers, cached data blocks, and Suspense-wrapped dynamic UI plus tag-based invalidation when data changes.
- Route architecture with static, cached, and Suspense dynamic segments
- cacheTag and updateTag invalidation plan for mutable data
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Cache Components are configured and coded while building Next.js routes—the canonical shelf before perf tuning at ship time. use cache, cacheLife, cacheTag, and Suspense boundaries are React/Next frontend architecture concerns on app routes.
Where it fits
Split a blog index into a static header and a use cache PostList fed by the database.
Tune cacheLife to hours for posts and Suspense-wrap UserPreferences so TTFB stays low under load.
Ship fast LCP on content pages while still streaming personalized nav after the cached article list.
Call updateTag after publishing so cached segments refresh without redeploying the whole site.
How it compares
Framework-native PPR and use cache patterns—not generic CDN cache headers or a one-off ISR cheat sheet from older Next docs.
Common Questions / FAQ
Who is next-cache-components for?
Solo developers shipping Next.js 16 App Router apps who want Partial Prerendering with explicit static, cached, and dynamic segments.
When should I use next-cache-components?
In Build when designing page architecture; in Ship perf when tuning cacheLife and tags; before Launch when OG-speed and freshness both matter on content routes.
Is next-cache-components safe to install?
It guides config and caching behavior—review the Security Audits panel on this page and validate cache invalidation does not expose private data in shared cached segments.
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