Skip to content

Docs IA 2.0: Add Caching and Revalidating page #79493

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
May 27, 2025
Merged
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
31 changes: 31 additions & 0 deletions docs/01-app/01-getting-started/08-fetching-data.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,37 @@ export default function BlogPage() {
}
```

## Deduplicating requests with `React.cache`

Deduplication is the process of _preventing duplicate requests_ for the same resource during a render pass. It allows you to fetch the same data in different components while preventing multiple network requests to your data source.

If you are using `fetch`, requests can be deduplicated by adding `cache: 'force-cache'`. This means you can safely call the same URL with the same options, and only one request will be made.

If you are _not_ using `fetch`, and instead using an ORM or database directly, you can wrap your data fetch with the [React `cache`](https://react.dev/reference/react/cache) function.

```tsx filename="app/lib/data.ts" switcher
import { cache } from 'react'
import { db, posts, eq } from '@/lib/db'

export const getPost = cache(async (id: string) => {
const post = await db.query.posts.findFirst({
where: eq(posts.id, parseInt(id)),
})
})
```

```jsx filename="app/lib/data.js" switcher
import { cache } from 'react'
import { db, posts, eq } from '@/lib/db'
import { notFound } from 'next/navigation'

export const getPost = cache(async (id) => {
const post = await db.query.posts.findFirst({
where: eq(posts.id, parseInt(id)),
})
})
```

## Streaming

> **Warning:** The content below assumes the [`dynamicIO` config option](/docs/app/api-reference/config/next-config-js/dynamicIO) is enabled in your application. The flag was introduced in Next.js 15 canary.
Expand Down
250 changes: 250 additions & 0 deletions docs/01-app/01-getting-started/09-caching-and-revalidating.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
---
title: How to cache and revalidate data
nav_title: Caching and Revalidating
description: Learn how to cache and revalidate data in your application.
related:
title: API Reference
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/revalidateTag
---

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)
- [`revalidateTag`](#revalidatetag)

### `fetch`

By default, [`fetch`](/docs/app/api-reference/functions/fetch) requests are not cached. You can cache individual requests by setting the `cache` option to `'force-cache'`.

```tsx filename="app/page.tsx" switcher
export default async function Page() {
const data = await fetch('https://...', { cache: 'force-cache' })
}
```

```jsx filename="app/page.jsx" switcher
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](/docs/app/getting-started/partial-prerendering#static-rendering) routes that have `fetch` requests and cache the HTML. If you want to guarantee a route is [dynamic](/docs/app/getting-started/partial-prerendering#dynamic-rendering), use the [`connection` API](/docs/app/api-reference/functions/connection).

To revalidate the data returned by a `fetch` request, you can use the `next.revalidate` option.

```tsx filename="app/page.tsx" switcher
export default async function Page() {
const data = await fetch('https://...', { next: { revalidate: 3600 } })
}
```

```jsx filename="app/page.jsx" switcher
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](/docs/app/api-reference/functions/fetch) 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:

```tsx filename="app/lib/data.ts swichter
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.jsx" 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.

### `revalidateTag`

`revalidateTag` is used to revalidate a cache entries based on a tag and following an event. 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:

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

export async function updateUser(id: string) {
// Mutate data
revalidateTag('user')
}
```

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

export async function updateUser(id) {
// Mutate data
revalidateTag('user')
}
```

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.
6 changes: 3 additions & 3 deletions docs/01-app/02-guides/migrating/app-router-migration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ export default function HomePage({ recentPosts }) {

- Create a new `app/page.tsx` file inside the `app` directory. This is a Server Component by default.
- Import the `home-page.tsx` Client Component into the page.
- If you were fetching data in `pages/index.js`, move the data fetching logic directly into the Server Component using the new [data fetching APIs](/docs/app/building-your-application/data-fetching/fetching). See the [data fetching upgrade guide](#step-6-migrating-data-fetching-methods) for more details.
- If you were fetching data in `pages/index.js`, move the data fetching logic directly into the Server Component using the new [data fetching APIs](/docs/app/getting-started/fetching-data). See the [data fetching upgrade guide](#step-6-migrating-data-fetching-methods) for more details.

```tsx filename="app/page.tsx" switcher
// Import your Client Component
Expand Down Expand Up @@ -556,7 +556,7 @@ export default function Dashboard({ projects }) {

In the App Router, we can colocate our data fetching inside our React components using [Server Components](/docs/app/getting-started/server-and-client-components). This allows us to send less JavaScript to the client, while maintaining the rendered HTML from the server.

By setting the `cache` option to `no-store`, we can indicate that the fetched data should [never be cached](/docs/app/building-your-application/data-fetching/fetching). This is similar to `getServerSideProps` in the `pages` directory.
By setting the `cache` option to `no-store`, we can indicate that the fetched data should [never be cached](/docs/app/getting-started/fetching-data). This is similar to `getServerSideProps` in the `pages` directory.

```tsx filename="app/dashboard/page.tsx" switcher
// `app` directory
Expand Down Expand Up @@ -876,7 +876,7 @@ export async function GET(request: Request) {}
export async function GET(request) {}
```

> **Good to know**: If you previously used API routes to call an external API from the client, you can now use [Server Components](/docs/app/getting-started/server-and-client-components) instead to securely fetch data. Learn more about [data fetching](/docs/app/building-your-application/data-fetching/fetching).
> **Good to know**: If you previously used API routes to call an external API from the client, you can now use [Server Components](/docs/app/getting-started/server-and-client-components) instead to securely fetch data. Learn more about [data fetching](/docs/app/getting-started/fetching-data).

#### Single-Page Applications

Expand Down
2 changes: 1 addition & 1 deletion docs/01-app/02-guides/production-checklist.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ While building your application, we recommend using the following features to en

<AppOnly>

- **[Server Components](/docs/app/building-your-application/data-fetching/fetching):** Leverage the benefits of fetching data on the server using Server Components.
- **[Server Components](/docs/app/getting-started/fetching-data):** Leverage the benefits of fetching data on the server using Server Components.
- **[Route Handlers](/docs/app/building-your-application/routing/route-handlers):** Use Route Handlers to access your backend resources from Client Components. But do not call Route Handlers from Server Components to avoid an additional server request.
- **[Streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming):** Use Loading UI and React Suspense to progressively send UI from the server to the client, and prevent the whole route from blocking while data is being fetched.
- **[Parallel Data Fetching](/docs/app/getting-started/fetching-data#parallel-data-fetching):** Reduce network waterfalls by fetching data in parallel, where appropriate. Also, consider [preloading data](/docs/app/getting-started/fetching-data#preloading-data) where appropriate.
Expand Down
Loading
Loading