Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
312 changes: 163 additions & 149 deletions docs/01-app/01-getting-started/09-caching-and-revalidating.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,23 @@ related:
description: Learn more about the features mentioned in this page by reading the API Reference.
links:
- app/api-reference/functions/fetch
- app/api-reference/functions/unstable_cache
- app/api-reference/functions/revalidatePath
- app/api-reference/functions/cacheTag
- app/api-reference/functions/revalidateTag
- app/api-reference/functions/updateTag
- app/api-reference/functions/revalidatePath
- app/api-reference/functions/unstable_cache
---

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`](#fetch)
- [`unstable_cache`](#unstable_cache)
- [`revalidatePath`](#revalidatepath)
- [`cacheTag`](#cachetag)
- [`revalidateTag`](#revalidatetag)
- [`updateTag`](#updatetag)
- [`revalidatePath`](#revalidatepath)
- [`unstable_cache`](#unstable_cache) (Legacy)

## `fetch`

Expand Down Expand Up @@ -56,103 +58,65 @@ export default async function Page() {

This will revalidate the data after a specified amount of seconds.

See the [`fetch` API reference](/docs/app/api-reference/functions/fetch) to learn more.

## `unstable_cache`
You can also tag `fetch` requests to enable on-demand cache invalidation:

`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:

```ts filename="app/lib/data.ts" switcher
import { db } from '@/lib/db'
```tsx filename="app/lib/data.ts" switcher
export async function getUserById(id: string) {
return db
.select()
.from(users)
.where(eq(users.id, id))
.then((res) => res[0])
const data = await fetch(`https://...`, {
next: {
tags: ['user'],
},
})
}
```

```jsx filename="app/lib/data.js" switcher
import { db } from '@/lib/db'

export async function getUserById(id) {
return db
.select()
.from(users)
.where(eq(users.id, id))
.then((res) => res[0])
const data = await fetch(`https://...`, {
next: {
tags: ['user'],
},
})
}
```

```tsx filename="app/page.tsx" highlight={2,11,13} switcher
import { unstable_cache } from 'next/cache'
import { getUserById } from '@/app/lib/data'
See the [`fetch` API reference](/docs/app/api-reference/functions/fetch) to learn more.

export default async function Page({
params,
}: {
params: Promise<{ userId: string }>
}) {
const { userId } = await params
## `cacheTag`

const getCachedUser = unstable_cache(
async () => {
return getUserById(userId)
},
[userId] // add the user ID to the cache key
)
}
```
[`cacheTag`](/docs/app/api-reference/functions/cacheTag) allows you to tag cached data in [Cache Components](/docs/app/getting-started/cache-components) so it can be revalidated on-demand. Previously, cache tagging was limited to `fetch` requests, and caching other work required the experimental `unstable_cache` API.

```jsx filename="app/page.js" highlight={2,7,9} switcher
import { unstable_cache } from 'next/cache'
import { getUserById } from '@/app/lib/data'
With Cache Components, you can use the [`use cache`](/docs/app/api-reference/directives/use-cache) directive to cache any computation, and `cacheTag` to tag it. This works with database queries, file system operations, and other server-side work.
Copy link
Collaborator Author

@icyJoseph icyJoseph Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably stress here, that fetch('...', { cache }) is not part of this story, it may be there, but w/ Cache Components thinking about fetch options is not necessary.


export default async function Page({ params }) {
const { userId } = await params
```tsx filename="app/lib/data.ts" switcher
import { cacheTag } from 'next/cache'

const getCachedUser = unstable_cache(
async () => {
return getUserById(userId)
},
[userId] // add the user ID to the cache key
)
export async function getProducts() {
'use cache'
cacheTag('products')

const products = await db.query('SELECT * FROM products')
return products
}
```

The function accepts a third optional object to define how the cache should be revalidated. It accepts:
```jsx filename="app/lib/data.js" switcher
import { cacheTag } from 'next/cache'

- `tags`: an array of tags used by Next.js to revalidate the cache.
- `revalidate`: the number of seconds after cache should be revalidated.
export async function getProducts() {
'use cache'
cacheTag('products')

```tsx filename="app/page.tsx" highlight={6-9} switcher
const getCachedUser = unstable_cache(
async () => {
return getUserById(userId)
},
[userId],
{
tags: ['user'],
revalidate: 3600,
}
)
const products = await db.query('SELECT * FROM products')
return products
}
```

```jsx filename="app/page.js" highlight={6-9} switcher
const getCachedUser = unstable_cache(
async () => {
return getUserById(userId)
},
[userId],
{
tags: ['user'],
revalidate: 3600,
}
)
```
Once tagged, you can use [`revalidateTag`](#revalidatetag) or [`updateTag`](#updatetag) to invalidate the cache entry for products.

See the [`unstable_cache` API reference](/docs/app/api-reference/functions/unstable_cache) to learn more.
> **Good to know**: `cacheTag` is used with [Cache Components](/docs/app/getting-started/cache-components) and the [`use cache`](/docs/app/api-reference/directives/use-cache) directive. It expands the caching and revalidation story beyond `fetch`.

See the [`cacheTag` API reference](/docs/app/api-reference/functions/cacheTag) to learn more.

## `revalidateTag`

Expand All @@ -161,55 +125,7 @@ See the [`unstable_cache` API reference](/docs/app/api-reference/functions/unsta
- **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:

```tsx filename="app/lib/data.ts" highlight={3-5} switcher
export async function getUserById(id: string) {
const data = await fetch(`https://...`, {
next: {
tags: ['user'],
},
})
}
```

```jsx filename="app/lib/data.js" highlight={3-5} switcher
export async function getUserById(id) {
const data = await fetch(`https://...`, {
next: {
tags: ['user'],
},
})
}
```

Alternatively, you can mark an `unstable_cache` function with the `tags` option:

```tsx filename="app/lib/data.ts" highlight={6-8} switcher
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'],
}
)
```

```jsx filename="app/lib/data.js" highlight={6-8} switcher
export const getUserById = unstable_cache(
async (id) => {
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](/docs/app/api-reference/file-conventions/route) or Server Action:
After tagging your cached data, using [`fetch`](#fetch) with `next.tags`, or the [`cacheTag`](#cachetag) function, you may call `revalidateTag` in a [Route Handler](/docs/app/api-reference/file-conventions/route) or Server Action:

```tsx filename="app/lib/actions.ts" highlight={1,5} switcher
import { revalidateTag } from 'next/cache'
Expand All @@ -233,28 +149,6 @@ You can reuse the same tag in multiple functions to revalidate them all at once.

See the [`revalidateTag` API reference](/docs/app/api-reference/functions/revalidateTag) to learn more.

## `revalidatePath`

`revalidatePath` is used to revalidate a route and following an event. To use it, call it in a [Route Handler](/docs/app/api-reference/file-conventions/route) or Server Action:

```tsx filename="app/lib/actions.ts" highlight={1} switcher
import { revalidatePath } from 'next/cache'

export async function updateUser(id: string) {
// Mutate data
revalidatePath('/profile')
```

```jsx filename="app/lib/actions.js" highlight={1} switcher
import { revalidatePath } from 'next/cache'

export async function updateUser(id) {
// Mutate data
revalidatePath('/profile')
```

See the [`revalidatePath` API reference](/docs/app/api-reference/functions/revalidatePath) 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.
Expand Down Expand Up @@ -307,3 +201,123 @@ The key differences between `revalidateTag` and `updateTag`:
- **`revalidateTag`**: In Server Actions and Route Handlers, supports stale-while-revalidate with `profile="max"`

See the [`updateTag` API reference](/docs/app/api-reference/functions/updateTag) to learn more.

## `revalidatePath`

`revalidatePath` is used to revalidate a route and following an event. To use it, call it in a [Route Handler](/docs/app/api-reference/file-conventions/route) or Server Action:

```tsx filename="app/lib/actions.ts" highlight={1} switcher
import { revalidatePath } from 'next/cache'

export async function updateUser(id: string) {
// Mutate data
revalidatePath('/profile')
```

```jsx filename="app/lib/actions.js" highlight={1} switcher
import { revalidatePath } from 'next/cache'

export async function updateUser(id) {
// Mutate data
revalidatePath('/profile')
```

See the [`revalidatePath` API reference](/docs/app/api-reference/functions/revalidatePath) to learn more.

## `unstable_cache`

> **Good to know**: `unstable_cache` is an experimental API. We recommend opting into [Cache Components](/docs/app/getting-started/cache-components) and replacing `unstable_cache` with the [`use cache`](/docs/app/api-reference/directives/use-cache) directive. See the [Cache Components documentation](/docs/app/getting-started/cache-components) for more details.

`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:

```ts filename="app/lib/data.ts" switcher
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])
}
```

```jsx filename="app/lib/data.js" switcher
import { db } from '@/lib/db'

export async function getUserById(id) {
return db
.select()
.from(users)
.where(eq(users.id, id))
.then((res) => res[0])
}
```

```tsx filename="app/page.tsx" highlight={2,11,13} switcher
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
)
}
```

```jsx filename="app/page.js" highlight={2,7,9} switcher
import { unstable_cache } from 'next/cache'
import { getUserById } from '@/app/lib/data'

export default async function Page({ params }) {
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.

```tsx filename="app/page.tsx" highlight={6-9} switcher
const getCachedUser = unstable_cache(
async () => {
return getUserById(userId)
},
[userId],
{
tags: ['user'],
revalidate: 3600,
}
)
```

```jsx filename="app/page.js" highlight={6-9} switcher
const getCachedUser = unstable_cache(
async () => {
return getUserById(userId)
},
[userId],
{
tags: ['user'],
revalidate: 3600,
}
)
```

See the [`unstable_cache` API reference](/docs/app/api-reference/functions/unstable_cache) to learn more.
Loading
Loading