Next.js 16 caching model expertise covering the 'use cache' directive, cacheLife() API, cacheTag() for invalidation, cacheComponents configuration, and Partial Prerendering (PPR). Use when implementing caching strategies in Next.js 16+ applications, migrating from unstable_cache, or optimizing server component rendering.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Next.js 16 caching model expertise covering the 'use cache' directive, cacheLife() API, cacheTag() for invalidation, cacheComponents configuration, and Partial Prerendering (PPR). Use when implementing caching strategies in Next.js 16+ applications, migrating from unstable_cache, or optimizing server component rendering.
Deep expertise on the Next.js 16 caching model. Covers the 'use cache' directive, cacheLife() profiles, cacheTag() invalidation, cacheComponents configuration, and Partial Prerendering (PPR) integration.
When to Apply
Use this skill when:
Implementing caching in a Next.js 16+ application
Migrating from unstable_cache or revalidate patterns to the new caching API
Configuring component-level caching with cacheComponents
Setting up cache invalidation with tags
Integrating Partial Prerendering (PPR) with cached components
Choosing between static generation, ISR, and dynamic rendering
Core Concepts
The Caching Paradigm Shift (Next.js 15 to 16)
Next.js 16 introduces a fundamentally new caching model:
Feature
Next.js 14
Next.js 15
Next.js 16
fetch() caching
Cached by default
Not cached by default
Not cached by default
Route caching
Automatic
Opt-in
'use cache' directive
Data caching
revalidate option
revalidate option
cacheLife() API
Invalidation
revalidateTag()
revalidateTag()
cacheTag() + revalidateTag()
Component caching
Not available
Experimental
cacheComponents: true
Key Principle
In Next.js 16, caching is explicit and opt-in. Nothing is cached unless you explicitly use the 'use cache' directive.
The 'use cache' Directive
Basic Usage
Add 'use cache' at the top of a file or function to enable caching:
// app/page.tsx -- cache the entire page'use cache';
() {
data = ();
posts = data.();
(
);
}
// Tag hierarchy for a blogcacheTag('blog'); // All blog contentcacheTag('blog', `blog-${slug}`); // Specific postcacheTag('blog', 'blog-comments'); // All commentscacheTag('blog', `blog-comments-${postId}`); // Post comments// Invalidate all blog contentrevalidateTag('blog');
// Invalidate just one postrevalidateTag(`blog-${slug}`);
Partial Prerendering (PPR) Integration
PPR combines static shells with dynamic holes, and 'use cache' works with it.
Cache public data but keep auth-dependent data dynamic:
// Cached: product data (same for all users)asyncfunctionProductInfo({ id }: { id: string }) {
'use cache';
cacheLife('hours');
cacheTag(`product-${id}`);
const product = awaitgetProduct(id);
return<ProductCardproduct={product} />;
}
// NOT cached: user-specific dataasyncfunctionUserCartStatus({ userId }: { userId: string }) {
// No 'use cache' -- always dynamicconst cart = awaitgetCart(userId);
return<CartBadgecount={cart.items.length} />;
}
Iron Laws
ALWAYS use 'use cache' explicitly on every component or function you intend to cache — in Next.js 16, nothing is cached unless you opt in; implicit caching assumptions from Next.js 14 are gone.
NEVER use 'use cache' on components that render user-specific or auth-dependent data — the cache key does not include session context; different users will receive each other's cached content.
ALWAYS call cacheTag() on every cached function that reads mutable data — without tags, there is no way to invalidate stale data after a mutation; the only recourse is waiting for expiry.
NEVER cache Server Actions that perform mutations — 'use cache' returns a cached response instead of executing the mutation; data changes are silently discarded.
ALWAYS call revalidateTag() in Server Actions or Route Handlers immediately after a mutation — forgetting invalidation means stale data persists for the full cache lifetime after every write.
Anti-Patterns
Anti-Pattern
Why It Fails
Correct Approach
Using 'use cache' on auth-dependent components
Cache key excludes session context; different users receive each other's cached data
Keep auth-dependent components dynamic; cache only public, user-agnostic data
Caching Server Actions that mutate data
Returns cached response instead of executing mutation; writes are silently discarded
Never put 'use cache' on mutation actions; only cache read operations
Missing cacheTag() on mutable data
No invalidation path; stale data persists until expiry with no way to purge on mutation
Always tag cached data: cacheTag('entity', 'entity-id')
Forgetting revalidateTag() after mutations
Stale data persists for full cache lifetime after every write
Call revalidateTag() in every Server Action or Route Handler that modifies data
Overly broad cache tag names
revalidateTag('all') invalidates the entire cache on every mutation — defeats purpose
Use granular hierarchical tags: 'products', 'product-{id}'