Documentation Index
Fetch the complete documentation index at: https://mintlify.com/cloudflare/vinext/llms.txt
Use this file to discover all available pages before exploring further.
The next/cache module provides APIs for controlling cache behavior, including time-based and tag-based revalidation, ISR, and the new Next.js 15+ "use cache" directive support.
Import
import {
revalidateTag,
revalidatePath,
updateTag,
refresh,
unstable_cache,
cacheLife,
cacheTag,
noStore,
} from 'next/cache'
import type {
CacheHandler,
CacheHandlerValue,
IncrementalCacheValue,
} from 'next/cache'
Cache Functions
revalidateTag
Revalidate cached data associated with a specific cache tag.
import { revalidateTag } from 'next/cache'
export async function revalidatePosts() {
revalidateTag('posts')
}
profile
string | { expire?: number }
Next.js 16+: cacheLife profile for stale-while-revalidate.revalidateTag('posts', 'hours') // Use 'hours' profile
revalidateTag('posts', { expire: 3600 }) // Custom expire time
Usage with fetch
// Tag a fetch request
const posts = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] }
})
// Later: revalidate all 'posts' fetches
revalidateTag('posts')
Usage with unstable_cache
const getCachedPosts = unstable_cache(
async () => getPosts(),
['posts-list'],
{ tags: ['posts'] }
)
// Later: revalidate all 'posts' cached functions
revalidateTag('posts')
revalidatePath
Revalidate cached data for a specific path.
import { revalidatePath } from 'next/cache'
export async function updatePost(id: string) {
await db.posts.update(id)
revalidatePath('/blog')
revalidatePath(`/blog/${id}`)
}
Path to revalidate (e.g., /blog, /products/123).
Next.js 14+: Revalidate type.
'page' — Revalidate specific page
'layout' — Revalidate layout and all children
Example: Server Action
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
const post = await db.posts.create({
title: formData.get('title'),
content: formData.get('content'),
})
// Revalidate the blog list page
revalidatePath('/blog')
return { success: true, id: post.id }
}
updateTag
Expire and immediately refresh cached data for a tag (Next.js 16+).
import { updateTag } from 'next/cache'
export async function updatePost(id: string) {
await db.posts.update(id)
updateTag('posts') // Expire immediately
}
Provides read-your-writes semantics: the cache entry is expired and fresh data is read within the same request, so users immediately see their changes.
Cache tag to expire and refresh.
refresh
Refresh uncached (dynamic) data on the page (Next.js 16+).
import { refresh } from 'next/cache'
export async function markAsRead(notificationId: string) {
await db.notifications.markAsRead(notificationId)
refresh() // Re-fetch dynamic data
}
Signals the client to re-fetch dynamic data without touching the cache. Useful for notification counts, live metrics, or status indicators.
noStore
Opt out of caching for the current component/function.
import { noStore } from 'next/cache'
export default async function RealTimePage() {
noStore() // Mark as dynamic
const data = await getRealTimeData()
return <div>{data.value}</div>
}
Equivalent to setting Cache-Control: no-store on the response. Also available as unstable_noStore().
unstable_cache
Wrap an async function with caching.
import { unstable_cache } from 'next/cache'
const getCachedUser = unstable_cache(
async (id: string) => {
return await db.users.findById(id)
},
['user'],
{
tags: ['user'],
revalidate: 3600 // 1 hour
}
)
const user = await getCachedUser('123')
Parameters
fn
(...args: any[]) => Promise<T>
required
Async function to wrap.
Cache key components. Combined with serialized arguments to form the final key.unstable_cache(
(id) => getUser(id),
['user'] // Final key: unstable_cache:user:{"id":"123"}
)
Cache options.interface UnstableCacheOptions {
revalidate?: number | false // Seconds, or false for no expiry
tags?: string[] // Cache tags for revalidation
}
Example: Cached API Call
import { unstable_cache } from 'next/cache'
const getCachedPosts = unstable_cache(
async () => {
const res = await fetch('https://api.example.com/posts')
return res.json()
},
['posts-api'],
{
revalidate: 60, // Revalidate every 60 seconds
tags: ['posts']
}
)
export default async function BlogPage() {
const posts = await getCachedPosts()
return <PostList posts={posts} />
}
”use cache” APIs (Next.js 15+)
cacheLife
Set cache lifetime for a "use cache" function.
'use cache'
import { cacheLife } from 'next/cache'
export async function getPosts() {
cacheLife('hours') // Use 'hours' profile
return await db.posts.findMany()
}
profile
string | CacheLifeConfig
required
Built-in profile name or custom config.Built-in profiles:
'default' — 15 min revalidate, ~49 day expire
'seconds' — 30s stale, 1s revalidate, 60s expire
'minutes' — 5 min stale, 1 min revalidate, 1h expire
'hours' — 5 min stale, 1h revalidate, 1 day expire
'days' — 5 min stale, 1 day revalidate, 1 week expire
'weeks' — 5 min stale, 1 week revalidate, 1 month expire
'max' — 5 min stale, 1 month revalidate, 1 year expire
Custom config:interface CacheLifeConfig {
stale?: number // Client cache duration (seconds)
revalidate?: number // Server revalidation interval (seconds)
expire?: number // Max staleness before deopt (seconds)
}
Example: Custom Profile
'use cache'
import { cacheLife } from 'next/cache'
export async function getPopularPosts() {
cacheLife({
stale: 60, // Client can cache for 60s
revalidate: 300, // Server refreshes every 5 min
expire: 3600 // Hard expire after 1 hour
})
return await db.posts.findMany({
orderBy: { views: 'desc' }
})
}
cacheTag
Tag a "use cache" function for revalidation.
'use cache'
import { cacheTag } from 'next/cache'
export async function getPosts() {
cacheTag('posts', 'blog')
return await db.posts.findMany()
}
// Later: revalidate
revalidateTag('posts')
Tags to attach to the cached result.
Cache Handler
Plug in a custom cache backend (Redis, DynamoDB, Cloudflare KV, etc.).
Interface
import type { CacheHandler, IncrementalCacheValue } from 'next/cache'
class MyCacheHandler implements CacheHandler {
async get(key: string): Promise<CacheHandlerValue | null> {
// Return cached entry or null
}
async set(key: string, data: IncrementalCacheValue | null): Promise<void> {
// Store cache entry
}
async revalidateTag(tags: string | string[]): Promise<void> {
// Invalidate entries with matching tags
}
}
Usage
import { setCacheHandler } from 'next/cache'
import { MyCacheHandler } from './cache'
// At server startup
setCacheHandler(new MyCacheHandler())
Built-in Handlers
MemoryCacheHandler
In-memory cache (default). Not shared across workers/instances.
import { MemoryCacheHandler } from 'next/cache'
Cloudflare KV Handler
vinext includes a KV-backed handler for Cloudflare Workers:
import { createKVCacheHandler } from 'vinext/cache/kv'
const handler = createKVCacheHandler(env.CACHE_KV)
setCacheHandler(handler)
CacheHandlerValue Type
interface CacheHandlerValue {
lastModified: number
age?: number
cacheState?: 'stale' | 'fresh'
value: IncrementalCacheValue | null
}
type IncrementalCacheValue =
| CachedFetchValue
| CachedAppPageValue
| CachedRouteValue
| CachedImageValue
ISR (Incremental Static Regeneration)
vinext supports time-based and tag-based ISR:
Time-Based Revalidation
// Revalidate every 60 seconds
export const revalidate = 60
export default async function Page() {
const posts = await getPosts()
return <PostList posts={posts} />
}
Tag-Based Revalidation
export default async function Page() {
const posts = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] }
})
return <PostList posts={await posts.json()} />
}
// In a Server Action:
import { revalidateTag } from 'next/cache'
export async function createPost() {
await db.posts.create(...)
revalidateTag('posts')
}
Stale-While-Revalidate
vinext’s ISR layer implements SWR:
- Fresh: Serve cached entry immediately
- Stale: Serve stale entry, trigger background revalidation
- Expired: Return null (or skip cache)
Cache Deduplication
Multiple concurrent requests for the same cache key are deduplicated:
// Only one fetch occurs, even if called 10 times simultaneously
const promises = Array.from({ length: 10 }, () => getCachedPosts())
await Promise.all(promises)
Limitations
Cache key collisions: If two functions use the same keyParts and arguments, they share a cache entry. Use unique keys.
Serialization: Arguments and return values are JSON-serialized. Functions, Symbols, and undefined are not supported.
Server-only: All cache APIs are server-side only. Calling them in client components will throw.
Source
View source code →
Implementation: /home/daytona/workspace/source/packages/vinext/src/shims/cache.ts