Next.js 15 introduced a more explicit and developer-friendly caching system.
But it's not automatic—caching only activates when you opt in using three mechanisms: enabling two flags (dynamicIO, useCache) in your config and adding the "use cache" directive inside a file, a component or a function. This triple-lock prevents accidental caching and gives you fine-grained control over revalidation and tagging.
Without this opt-in model, stale content can easily sneak into your UI. Now, you’re in full control. You decide what gets cached, for how long, and what tags are used for easy invalidation.
To start using the new cache layer, you need to activate two experimental flags in next.config.js, and then opt in at the file or function level using the "use cache" directive:
// next.config.jsmodule.exports = {experimental: {dynamicIO: true,useCache: true}}
Then, inside your route:
// app/products/page.js'use cache'export default async function ProductPage() {const data = await fetch('/api/products', {next: { tags: ['products'] }})return}
Use the built-in functions below to fine-tune caching behavior:
cacheLife(seconds): Sets a time-to-live (TTL), similar to ISR’s revalidate.cacheTag(tag): Associates a label (e.g. user-123) to allow grouped invalidation.revalidateTag(tag): Clears all entries with that tag on next request—ideal for post-write updates.cacheLife(3600)cacheTag(`user-${id}`)revalidateTag('products')export async function POST() {await updateDB()revalidateTag('products')}
dynamicIOWhen using dynamicIO, some core Next.js behaviors change:
cookies() and headers() become async functions:import { cookies } from 'next/headers'const token = (await cookies()).get('token')
export const dynamic = 'force-static'
if you want to guarantee build-time rendering.
Use hybrid caching for best results:
'use cache'export default async function Dashboard() {const staticData = await fetch('/api/metrics', { cacheLife: 3600 })const liveData = await fetch('/api/activity', { cache: 'no-store' })return}cacheTag(`user-${crypto.randomUUID()}`)
✅ Figure: Good example - Mixing real-time and cached content gives you performance and freshness
staleTime: 0 by default—navigations recheck the server.You can learn more in depth about how to cache with Next.js in the official documentation.
Avoid caching components that depend on non-serializable props (e.g. children):
'use cache'function SafeCache({ children }) {return <section>{children}</section>}
To inspect what was cached or missed:
NEXTJS_CACHE_ANALYTICS=1 next build
This emits a hit/miss report for each route to the console, giving visibility into caching behavior.