Caching and Revalidating
Caching is a technique for storing the result of data fetching and other computations so that future requests for the same data can be served faster, without doing the work again. While revalidation allows you to update cache entries without having to rebuild your entire application.
Next.js provides a few APIs to handle caching and revalidation. This guide will walk you through when and how to use them.
fetch
By default, fetch
requests are not cached. You can cache individual requests by setting the cache
option to 'force-cache'
.
export default async function Page() {
const data = await fetch('https://...', { cache: 'force-cache' })
}
Good to know: Although
fetch
requests are not cached by default, Next.js will prerender routes that havefetch
requests and cache the HTML. If you want to guarantee a route is dynamic, use theconnection
API.
To revalidate the data returned by a fetch
request, you can use the next.revalidate
option.
export default async function Page() {
const data = await fetch('https://...', { next: { revalidate: 3600 } })
}
This will revalidate the data after a specified amount of seconds.
See the fetch
API reference to learn more.
unstable_cache
unstable_cache
allows you to cache the result of database queries and other async functions. To use it, wrap unstable_cache
around the function. For example:
import { db } from '@/lib/db'
export async function getUserById(id: string) {
return db
.select()
.from(users)
.where(eq(users.id, id))
.then((res) => res[0])
}
import { unstable_cache } from 'next/cache'
import { getUserById } from '@/app/lib/data'
export default async function Page({
params,
}: {
params: Promise<{ userId: string }>
}) {
const { userId } = await params
const getCachedUser = unstable_cache(
async () => {
return getUserById(userId)
},
[userId] // add the user ID to the cache key
)
}
The function accepts a third optional object to define how the cache should be revalidated. It accepts:
tags
: an array of tags used by Next.js to revalidate the cache.revalidate
: the number of seconds after cache should be revalidated.
const getCachedUser = unstable_cache(
async () => {
return getUserById(userId)
},
[userId],
{
tags: ['user'],
revalidate: 3600,
}
)
See the unstable_cache
API reference to learn more.
revalidateTag
revalidateTag
is used to revalidate cache entries based on a tag and following an event. The function now supports two behaviors:
- With
profile="max"
: Uses stale-while-revalidate semantics, serving stale content while fetching fresh content in the background - Without the second argument: Legacy behavior that immediately expires the cache (deprecated)
To use it with fetch
, start by tagging the function with the next.tags
option:
export async function getUserById(id: string) {
const data = await fetch(`https://...`, {
next: {
tags: ['user'],
},
})
}
Alternatively, you can mark an unstable_cache
function with the tags
option:
export const getUserById = unstable_cache(
async (id: string) => {
return db.query.users.findFirst({ where: eq(users.id, id) })
},
['user'], // Needed if variables are not passed as parameters
{
tags: ['user'],
}
)
Then, call revalidateTag
in a Route Handler or Server Action:
import { revalidateTag } from 'next/cache'
export async function updateUser(id: string) {
// Mutate data
revalidateTag('user', 'max') // Recommended: Uses stale-while-revalidate
}
You can reuse the same tag in multiple functions to revalidate them all at once.
See the revalidateTag
API reference to learn more.
revalidatePath
revalidatePath
is used to revalidate a route and following an event. To use it, call it in a Route Handler or Server Action:
import { revalidatePath } from 'next/cache'
export async function updateUser(id: string) {
// Mutate data
revalidatePath('/profile')
See the revalidatePath
API reference to learn more.
updateTag
updateTag
is specifically designed for Server Actions to immediately expire cached data for read-your-own-writes scenarios. Unlike revalidateTag
, it can only be used within Server Actions and immediately expires the cache entry.
import { updateTag } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
// Create post in database
const post = await db.post.create({
data: {
title: formData.get('title'),
content: formData.get('content'),
},
})
// Immediately expire cache so the new post is visible
updateTag('posts')
updateTag(`post-${post.id}`)
redirect(`/posts/${post.id}`)
}
The key differences between revalidateTag
and updateTag
:
updateTag
: Only in Server Actions, immediately expires cache, for read-your-own-writesrevalidateTag
: In Server Actions and Route Handlers, supports stale-while-revalidate withprofile="max"
See the updateTag
API reference to learn more.
API Reference
fetch
unstable_cache
revalidatePath
revalidateTag
updateTag
Was this helpful?