use cache
The use cache
directive designates a component, function, or file to be cached. It can be used at the top of a file to indicate that all functions in the file are cacheable, or inline at the top of a function to mark the function as cacheable. This is an experimental Next.js feature, and not a native React feature like use client
or use server
.
Enable support for the use cache
directive with the dynamicIO
flag in your next.config.ts
file:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
dynamicIO: true,
},
}
export default nextConfig
The
use cache
directive will be available separately from thedynamicIO
flag in the future.
Caching is a technique used to improve the performance of web applications by storing the results of rendering or data requests. Whenever you use an asynchronous function or APIs that depend on request-time data, Next.js will automatically opt into dynamic rendering. You can explicitly cache the results of these operations and optimize your application's rendering performance with the use cache
directive.
The use cache
directive is an experimental feature that aims to replace the unstable_cache
function. Unlike unstable_cache
, which is limited to caching JSON data and requires manual definition of revalidation periods and tags, use cache
offers more flexibility. It allows you to cache a wider range of data, including anything that React Server Components (RSC) can serialize, as well as data-fetching outputs and component outputs.
Additionally, use cache
automatically manages complexities by tracking both inputs and outputs, making it less likely for you to accidentally poison your cache. Since it serializes both inputs and outputs, you can avoid issues with incorrect cache retrieval.
use cache
directive
The Next.js use cache
directive allows you to cache entire routes, components, and the return value of functions. When you have an asynchronous function, you can mark it as cacheable by adding use cache
at the top of the file or inside the function scope. This informs Next.js that the return value can be cached and reused for subsequent renders.
// File level
'use cache'
export default async function Page() {
// ...
}
// Component level
export async function MyComponent() {
'use cache'
return <></>
}
// Function level
export async function getData() {
'use cache'
const data = await fetch('/api/data')
return data
}
Good to know: Functions that use the
use cache
directive must not have any side-effects, such as modifying state, directly manipulating the DOM, or setting timers to execute code at intervals
Revalidating
By default, Next.js sets a revalidation period of 15 minutes when you use the use cache
directive. Next.js sets a with a near-infinite expiration duration, meaning it's suitable for content that doesn't need frequent updates.
While this revalidation period may be useful for content you don't expect to change often, you can use the cacheLife
and cacheTag
APIs to configure the cache behavior:
Both of these APIs integrate across the client and server caching layers, meaning you can configure your caching semantics in one place and have them apply everywhere.
Basic example:
The example below shows how to use the cacheLife
function at the function level to set a revalidation period of one day on the functions output:
import { unstable_cacheLife as cacheLife } from 'next/cache'
export async function MyComponent() {
async function getData() {
'use cache'
cacheLife('days')
const data = await fetch('/api/data')
return data
}
return // Use the data here
}
How cache revalidation works
When a revalidation period of fifteen minutes is set, the following happens:
- Cache HIT: If a request is made within the 15 minute window, the cached data is served, and is a cache HIT.
- Stale data: If the request happens after 15 minutes the cached value is still served, but is now considered stale. Next.js will recompute a new cache entry in the background.
- Cache MISS: If the cache entry expires and a subsequent request is made, then Next.js will treat this as a cache MISS, and the data will be recomputed and fetched again from the source.
Time-based revalidation with cacheLife
The cacheLife
function can only be used where the use cache
directive is present, and allows you to define time-based revalidation periods based on cache profiles.
We recommend always adding a cache profile when using the use cache
directive to explicitly define caching behavior.
Cache profiles are objects that contain the following properties:
Property | Value | Description | Requirement |
---|---|---|---|
stale | number | Duration the client should cache a value without checking the server. | Optional |
revalidate | number | Frequency at which the cache should refresh on the server; stale values may be served while revalidating. | Optional |
expire | number | Maximum duration for which a value can remain stale before switching to dynamic fetching; must be longer than revalidate . | Optional - Must be longer than revalidate |
The "stale" property differs from the staleTimes
setting in that it specifically controls client-side router caching. While staleTimes
is a global setting that affects all instances of both dynamic and static data, the cacheLife
configuration allows you to define "stale" times on a per-function or per-route basis.
Good to know: The “stale” property does not set the
Cache-control: max-age
header. It instead controls the client-side router cache.
Default cache profiles
Next.js provides a set of named cache profiles modeled on various timescales. If you don't specify a cache profile in the cacheLife
function alongside the use cache
directive, Next.js will automatically apply the “default” cache profile.
Profile | Stale | Revalidate | Expire | Description |
---|---|---|---|---|
default | undefined | 15 minutes | INFINITE_CACHE | Default profile, suitable for content that doesn't need frequent updates |
seconds | undefined | 1 second | 1 minute | For rapidly changing content requiring near real-time updates |
minutes | 5 minutes | 1 minute | 1 hour | For content that updates frequently within an hour |
hours | 5 minutes | 1 hour | 1 day | For content that updates daily but can be slightly stale |
days | 5 minutes | 1 day | 1 week | For content that updates weekly but can be a day old |
weeks | 5 minutes | 1 week | 1 month | For content that updates monthly but can be a week old |
max | 5 minutes | 1 month | INFINITE_CACHE | For very stable content that rarely needs updating |
Basic example:
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'
cacheLife('minutes')
The string values used to reference cache profiles don't carry inherent meaning; instead they serve as semantic labels. This allows you to better understand and manage your cached content within your codebase.
Defining reusable cache profiles
You can create a reusable cache profile by defining them in your next.config.ts
file. Choose a name that suits your use case and set values for the stale
, revalidate
, and expire
properties. You can create as many custom cache profiles as needed. Each profile can be referenced by its name as a string value passed to the cacheLife
function.
const nextConfig = {
experimental: {
dynamicIO: true,
cacheLife: {
biweekly: {
stale: 60 * 60 * 24 * 14, // 14 days
revalidate: 60 * 60 * 24, // 1 day
expire: 60 * 60 * 24 * 14, // 14 days
},
},
},
}
module.exports = nextConfig
The example above caches for 14 days, checks for updates daily, and expires the cache after 14 days. You can then reference this profile throughout your application by its name:
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'
cacheLife('biweekly')
// rest of code
Overriding the default cache profiles
While the default cache profiles provide a useful way to think about how fresh or stale any given part of cacheable output can be, you may prefer different named profiles to better align with your applications caching strategies.
You can override the default named cache profiles by creating a new configuration with the same name as the defaults.
The example below shows how to override the default “days” cache profile:
const nextConfig = {
experimental: {
dynamicIO: true,
cacheLife: {
days: {
stale: 3600, // 1 hour
revalidate: 900, // 15 minutes
expire: 86400, // 1 day
},
},
},
}
module.exports = nextConfig
Defining cache profiles inline
For specific use cases, you can set a custom cache profile by passing an object to the cacheLife
function:
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'
cacheLife({
stale: 3600, // 1 hour
revalidate: 900, // 15 minutes
expire: 86400, // 1 day
})
// rest of code
This inline cache profile will only be applied to the function or file it was created in. If you want to reuse the same profile throughout your application, you can add the configuration to the cacheLife
property of your next.config.ts
file.
Nested usage of use cache
and cacheLife
When defining multiple caching behaviors in the same route or component tree, if the inner caches specify their own cacheLife
profile, the outer cache will respect the shortest cache duration among them. This applies only if the outer cache does not have its own explicit cacheLife
profile defined.
Decision hierarchy for cache boundaries:
- Next.js will use the shortest cache profile found within the whole
use cache
boundary, excluding inneruse cache
directives. - If no cache profile exists, then the shortest profile times from all inner
use cache
calls applies to thisuse cache
. If there are no inneruse cache
's then the default is used - Inner caches at two levels deep do not affect the outer cache since they have already provided their duration to their parent.
For example, if you add the use cache
directive to your page, without specifying a cache profile, the default cache profile will be applied implicitly (cacheLife(”default”)
). If a component imported into the page also uses the use cache
directive with its own cache profile, the outer and inner cache profiles are compared, and shortest duration set in the profiles will be applied.
// Parent component
import { unstable_cacheLife as cacheLife } from 'next/cache'
import { ChildComponent } from './child'
export async function ParentComponent() {
'use cache'
cacheLife('days')
return (
<div>
<ChildComponent />
</div>
)
}
And in a separate file, we defined the Child component that was imported:
// Child component
import { unstable_cacheLife as cacheLife } from 'next/cache'
export async function ChildComponent() {
'use cache'
cacheLife('hours')
return <div>Child Content</div>
// This component's cache will respect the shorter 'hours' profile
}
Revalidate on-demand with cacheTag
A cacheTag
is used in combination with revalidateTag
to purge cache data, on-demand. The cacheTag
function takes a single string value, or a string array.
In the following example,the getData
function uses the “weeks” cache profile, and defines a cacheTag
on the functions cached output:
import {
unstable_cacheTag as cacheTag,
unstable_cacheLife as cacheLife,
} from 'next/cache'
export async function getData() {
'use cache'
cacheLife('weeks')
cacheTag('my-data')
const data = await fetch('/api/data')
return data
}
You can then purge the cache on-demand using revalidateTag
API in another function, for example, a route handler or Server Action:
'use server'
import { revalidateTag } from 'next/cache'
export default async function submit() {
await addPost()
revalidateTag('my-data')
}
See the revalidateTag
docs for more information on purging cached data on-demand.
Examples
Caching entire routes with use cache
The placement of Suspense
boundaries in your application determines how dynamic your components can be. Components inside a Suspense
boundary are allowed to be dynamic, but this doesn't mean they automatically are. If you cache everything or your content is static, Next.js will still generate a static application. Using Suspense
indicates that dynamic behavior is allowed within the boundary.
To ensure your route remains static, avoid using Suspense
boundaries. If you must use them, you can maintain a static page by adding the use cache
directive to both the layout and page components as they are treated as separate entry points in your application.
This is recommended for applications that previously used the export const dynamic = "force-cache"
option, and will ensure the entire route is prerendered.
"use cache"
import { unstable_cacheLife as cacheLife } from 'next/cache'
cacheLife('minutes')
export default Layout({children}: {children: ReactNode}) {
return <div>{children}</div>
}
And in your page.tsx
file you can add the use cache
directive to the top of the file, and define a cache profile:
"use cache"
import { unstable_cacheLife as cacheLife } from 'next/cache'
cacheLife('minutes')
async function Users() {
const users = await fetch('/api/users');
// loop through users
}
export default Page() {
return (
<main>
<Users/>
</main>
)
}
Caching component output with use cache
You can use use cache
at the component level to cache any fetches or computations performed within that component. When you reuse the component throughout your application it can share the same cache entry as long as the props maintain the same structure.
The props are serialized and form part of the cache key. If you use the same component in multiple places in your application, the cache entry will be reused as long as the serialized props produce the same value in each instance.
import { unstable_cacheLife as cacheLife } from 'next/cache'
interface BookingsProps {
type: string
}
export async function Bookings({ type = 'massage' }: BookingsProps) {
'use cache'
cacheLife('minutes')
async function getBookingsData() {
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
return data
}
return //...
}
Caching function output with use cache
Since you can add use cache
to any asynchronous function you aren't limited to caching components or routes only. You might want to cache a network request or database query or compute something that is very slow. By adding use cache
to a function containing this type of work it becomes cacheable, and when reused, will share the same cache entry.
import { unstable_cacheLife as cacheLife } from 'next/cache'
export async function getData() {
'use cache'
cacheLife('minutes')
const data = await fetch('/api/data')
return data
}
Related
dynamicIO
cacheLife
cacheTag
revalidateTag
unstable_cache
Was this helpful?