Skip to content
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

Docs: Improve caching story #73437

Open
wants to merge 34 commits into
base: canary
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f2e3ee1
Clean up
delbaoliveira Dec 2, 2024
9143654
Create 07-caching-and-revalidating.mdx
delbaoliveira Dec 2, 2024
3b820a3
Add `caching` section (wip)
delbaoliveira Dec 2, 2024
b72324b
Add API reference description
delbaoliveira Dec 6, 2024
a8e6cfe
Clean up
delbaoliveira Dec 6, 2024
8f1c93a
Add caching examples
delbaoliveira Dec 6, 2024
4bebd04
Merge branch 'canary' into docs-getting-started-caching-and-revalidating
delbaoliveira Dec 6, 2024
7254afb
Add revalidation section
delbaoliveira Dec 6, 2024
23581d5
Add time-based revalidation examples
delbaoliveira Dec 6, 2024
9bf78d8
Merge branch 'canary' into docs-getting-started-caching-and-revalidating
delbaoliveira Dec 11, 2024
3a4a981
Clean up
delbaoliveira Dec 11, 2024
1fbc5fd
Add on-demand revalidation example
delbaoliveira Dec 11, 2024
d79be66
Clean up
delbaoliveira Dec 11, 2024
5d0eaa0
Remove mentions of expireTag and expirePath
delbaoliveira Dec 11, 2024
5481a23
Clean up
delbaoliveira Dec 11, 2024
2237db5
Merge branch 'canary' into docs-getting-started-caching-and-revalidating
delbaoliveira Dec 11, 2024
8c78cc0
Update caching description
delbaoliveira Dec 17, 2024
fb5e279
Use api.vercel.app
delbaoliveira Dec 17, 2024
b6cf0a1
Update docs/01-app/01-getting-started/07-caching-and-revalidating.mdx
delbaoliveira Dec 17, 2024
eebe58c
Clarify that functions within a file are cached separately
delbaoliveira Dec 17, 2024
6d9cd97
Update docs/01-app/01-getting-started/07-caching-and-revalidating.mdx
delbaoliveira Dec 17, 2024
b690c1e
Merge branch 'docs-getting-started-caching-and-revalidating' of https…
delbaoliveira Dec 17, 2024
f5a8342
Clean up
delbaoliveira Dec 17, 2024
b33b5ed
Merge branch 'canary' into docs-getting-started-caching-and-revalidating
delbaoliveira Dec 17, 2024
89db02d
clean up
delbaoliveira Dec 17, 2024
03b1ad2
Merge branch 'canary' into docs-getting-started-caching-and-revalidating
delbaoliveira Dec 17, 2024
1b8a681
Merge branch 'canary' into docs-getting-started-caching-and-revalidating
delbaoliveira Jan 6, 2025
4111b1a
Merge branch 'canary' into docs-getting-started-caching-and-revalidating
delbaoliveira Jan 6, 2025
2a9effe
Merge branch 'canary' into docs-getting-started-caching-and-revalidating
delbaoliveira Mar 3, 2025
c839e79
Add "How `use cache` works" section
delbaoliveira Mar 4, 2025
8d9e017
Move revalidation section up
delbaoliveira Mar 4, 2025
66559c9
Remove repetition
delbaoliveira Mar 4, 2025
03e4bc7
Clean up
delbaoliveira Mar 4, 2025
4b2892e
grammar
delbaoliveira Mar 4, 2025
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
299 changes: 299 additions & 0 deletions docs/01-app/01-getting-started/07-caching-and-revalidating.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
---
title: How to cache and revalidate components and functions
nav_title: Caching and Revalidating
description: Learn how to cache and revalidate components and functions in your Next.js application.
related:
title: API Reference
description: Learn more about the features mentioned in this page by reading the API Reference.
links:
- app/api-reference/config/next-config-js/dynamicIO
- app/api-reference/directives/use-cache
- app/api-reference/functions/cacheLife
- app/api-reference/functions/cacheTag
- app/api-reference/functions/revalidateTag
- app/api-reference/functions/revalidatePath
---

> **Warning:** The content below assumes the [`dynamicIO` config option](/docs/app/api-reference/config/next-config-js/dynamicIO) is enabled in your application. This option was introduced in Next.js 15 canary.

## Caching

In Next.js, you can cache the output of a [component](#components) or [function](#functions) to reduce the need to re-execute work for every user request.

Caching is useful for data that doesn't change often or is shared across multiple users, as you can **prerender** at build time, and reuse the static content for all requests.

To mark a function or component as cacheable, you can use the [`'use cache'`](/docs/app/api-reference/directives/use-cache) directive.

### Components

You can add `'use cache'` to an **asynchronous** [Server Component](https://react.dev/reference/rsc/server-components) to cache the component's render output:

```tsx filename="app/blog/page.tsx" highlight={4} switcher
import { getPosts } from '@/app/lib/data'

export default async function Page() {
'use cache'
const posts = await getPosts()

return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```

```js filename="app/blog/page.js" highlight={4} switcher
import { getPosts } from '@/app/lib/data'

export default async function Page() {
'use cache'
const posts = await getPosts()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```

Alternatively, you can add the `'use cache'` directive at the top of the file to mark all components in the file as cacheable:

```tsx filename="app/blog/page.tsx" highlight={1} switcher
'use cache'

import { getPosts } from '@/app/lib/data'

export default async function Page() {
const posts = await getPosts()

return <Posts posts={posts} />
}

async function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```

```js filename="app/blog/page.js" highlight={1} switcher
'use cache'

import { getPosts } from '@/app/lib/data'

export default async function Page() {
const posts = await getPosts()

return <Posts posts={posts} />
}

async function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```

### Functions

You can cache the return value of an **asynchronous** function, including data requests, by adding the `'use cache'` directive inside the function body:

```ts filename="app/lib/data.ts" switcher
export async function getPosts(slug: string) {
'use cache'
const data = await fetch(`/api/posts/${slug}`)
return data.json()
}
```

```js filename="app/lib/data.js" switcher
export async function getPosts(slug) {
'use cache'
const data = await fetch(`/api/posts/${slug}`)
return data.json()
}
```

With the approach above, you can call the function throughout your application code and the same cache entry will be reused as long as the arguments are the same.

Alternatively, you can also cache all functions within a file by adding the `'use cache'` directive at the top of the file:

```ts filename="app/lib/data.ts" switcher
'use cache'

export async function getPosts() {
const data = await fetch(`/api/posts`)
return data.json()
}

export async function getPostBySlug(slug: string) {
const data = await fetch(`/api/posts/${slug}`)
return data.json()
}
```

```js filename="app/lib/data.js" switcher
'use cache'

export async function getPosts() {
const data = await fetch(`/api/posts/`)
return data.json()
}

export async function getPostBySlug(slug: string) {
const data = await fetch(`/api/posts/${slug}`)
return data.json()
}
```

Copy link
Member

Choose a reason for hiding this comment

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

Maybe again: There will be two separate entries into the cache. One for getPosts and another for getPostsBySlug, where the cache will be invalidated if the slug argument changes.

Copy link
Contributor Author

@delbaoliveira delbaoliveira Dec 17, 2024

Choose a reason for hiding this comment

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

For the invalidated part, wouldn't it be two separate entries? A cache key is the function's unique id + the arguments?
https://vercel.slack.com/archives/C06QRHBQ742/p1730766893478919?thread_ts=1730765761.544979&cid=C06QRHBQ742

## Revalidating

Revalidation allows you to update cached content without having to rebuild your entire application. It's useful for content that _sometimes_ changes, but would benefit from being cached to improve your application'sperformance.

In Next.js, there are two types of revalidation:

- [Time-based](#time-based-revalidation): Based on a time interval (e.g. every hour).
- [On-demand](#on-demand-revalidation): Triggered by a specific event (e.g. a CMS webhook).

## Time-based Revalidation

You can use the [`cacheLife` function](/docs/app/api-reference/functions/cacheLife) to define a time interval for how long a cached value should remain stale before it's revalidated.

`cacheLife` comes with [default cache profiles](/docs/app/api-reference/functions/cacheLife#default-cache-profiles) such as `'hour'`, `'day'`, and `'week'`. These profiles can be [customized](/docs/app/api-reference/functions/cacheLife#custom-cache-profiles), if needed.

To use `cacheLife`, import it from `next/cache` and nest it within the **scope** of the `'use cache'` directive and the function. For example, to revalidate the blog page after one hour:

```tsx filename="app/blog/page.tsx" highlight={3,6} switcher
'use cache'o/>:.

import { cacheLife } from 'next/cache'

export default async function Page() {
cacheLife('hour')
return <Posts posts={posts} />
}
```

```jsx filename="app/blog/page.js" highlight={3,6} switcher
'use cache'

import { cacheLife } from 'next/cache'

export default async function Page() {
cacheLife('hour')
return <Posts posts={posts} />
}
```

## On-demand Revalidation

You can use the [`cacheTag` function](/docs/app/api-reference/functions/cacheTag) to define a tag for a cache entry. The entry can then be revalidated using the [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) function in another part of your application, such as a [Server Action](https://react.dev/reference/rsc/server-functions) or [Route Handler](/docs/app/building-your-application/routing/route-handlers).

For example, to update the list of blog posts once a new post is created, add the `cacheTag` function to the component that renders the list of posts:

```tsx filename="app/ui/posts.tsx" switcher
'use cache'

import { cacheTag } from 'next/cache'

export function Posts({ posts }) {
cacheTag('blog-posts')
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```

```jsx filename="app/ui/posts.js" switcher
'use cache'

import { cacheTag } from 'next/cache'

export function Posts({ posts }) {
cacheTag('blog-posts')
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```

Then, in the Server Action to create a new post, call `revalidateTag` using the same tag to purge the cache entry:

```tsx filename="app/actions.ts" switcher
'use server'

import { revalidateTag } from 'next/cache'

export async function createPost() {
// Mutate data
// ...

// Purge cache
revalidateTag('blog-posts')
}
```

```jsx filename="app/actions.js" switcher
'use server'

import { revalidateTag } from 'next/cache'

export async function createPost() {
// Mutate data
// ...

// Purge cache
revalidateTag('blog-posts')
}
```

Alternatively, you can call the [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) function to purge the whole `/blog` route (in which case you don't need `cacheTag`):

```tsx filename="app/actions.ts" switcher
'use server'

import { revalidatePath } from 'next/cache'

export async function createPost() {
// Mutate data
// ...

// Purge cache
revalidatePath('/blog')
}
```

```tsx filename="app/actions.ts" switcher
'use server'

import { revalidatePath } from 'next/cache'

export async function createPost() {
// Mutate data
// ...

// Purge cache
expirePath('/blog')
}
```
4 changes: 1 addition & 3 deletions docs/01-app/03-api-reference/01-directives/use-cache.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,12 @@ export async function getData() {

## Examples

### Caching entire routes with `use cache`
### Caching an entire route with `use cache`

To prerender an entire route, add `use cache` to the top **both** the `layout` and `page` files. Each of these segments are treated as separate entry points in your application, and will be cached independently.

```tsx filename="app/layout.tsx" switcher
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'

export default function Layout({ children }: { children: ReactNode }) {
return <div>{children}</div>
Expand All @@ -84,7 +83,6 @@ export default function Layout({ children }: { children: ReactNode }) {

```jsx filename="app/page.tsx" switcher
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'

export default function Layout({ children }) {
return <div>{children}</div>
Expand Down
Loading