diff --git a/docs/01-app/01-getting-started/07-caching-and-revalidating.mdx b/docs/01-app/01-getting-started/07-caching-and-revalidating.mdx new file mode 100644 index 0000000000000..ea2f17698ae1b --- /dev/null +++ b/docs/01-app/01-getting-started/07-caching-and-revalidating.mdx @@ -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 users, and functions that are computationally intensive, as you can reuse the result and reduce the time it takes to respond to 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 ( + + ) +} +``` + +```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 ( + + ) +} +``` + +Alternatively, you can add the `'use cache'` directive at the top of the file to mark all components in the file as cacheable. Each component will be cached separately, with its own cache key. + +```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 +} + +async function Posts({ posts }) { + return ( + + ) +} +``` + +```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 +} + +async function Posts({ posts }) { + return ( + + ) +} +``` + +### 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(`https://api.vercel.app/posts/${slug}`) + return data.json() +} +``` + +```js filename="app/lib/data.js" switcher +export async function getPosts(slug) { + 'use cache' + const data = await fetch(`https://api.vercel.app/posts/${slug}`) + return data.json() +} +``` + +You can then call the function throughout your application code and the same cache entry will be reused, as long as the arguments to the function (e.g. `slug`) are the same. If the arguments are different, a new cache key will be created for the new arguments. + +Alternatively, you can also cache all functions within a file by adding the `'use cache'` directive at the top of the file. Each function will be cached separately. + +```ts filename="app/lib/data.ts" switcher +'use cache' + +export async function getPosts() { + const data = await fetch(`https://api.vercel.app/posts`) + return data.json() +} + +export async function getPostBySlug(slug: string) { + const data = await fetch(`https://api.vercel.app/posts/${slug}`) + return data.json() +} +``` + +```js filename="app/lib/data.js" switcher +'use cache' + +export async function getPosts() { + const data = await fetch(`https://api.vercel.app/posts/`) + return data.json() +} + +export async function getPostBySlug(slug: string) { + const data = await fetch(`https://api.vercel.app/posts/${slug}`) + return data.json() +} +``` + +## 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's performance. + +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' + +import { cacheLife } from 'next/cache' + +export default async function Page() { + cacheLife('hour') + return +} +``` + +```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 +} +``` + +## 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 ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +```jsx filename="app/ui/posts.js" switcher +'use cache' + +import { cacheTag } from 'next/cache' + +export function Posts({ posts }) { + cacheTag('blog-posts') + return ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} +``` + +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') +} +``` diff --git a/docs/01-app/04-api-reference/01-directives/use-cache.mdx b/docs/01-app/04-api-reference/01-directives/use-cache.mdx index f29085775dae7..bd16c95063b9e 100644 --- a/docs/01-app/04-api-reference/01-directives/use-cache.mdx +++ b/docs/01-app/04-api-reference/01-directives/use-cache.mdx @@ -14,11 +14,11 @@ related: - app/api-reference/functions/revalidateTag --- -The `use cache` directive designates a component and/or a function to be cached. It can be used at the top of a file to indicate that all exports in the file are cacheable, or inline at the top of a function or component to inform Next.js the return value should be cached and reused for subsequent requests. This is an experimental Next.js feature, and not a native React feature like [`use client`](/docs/app/api-reference/directives/use-client) or [`use server`](/docs/app/api-reference/directives/use-server). +The `use cache` directive allows you to mark a component or a function as cacheable. It can be used at the top of a file to indicate that all exports in the file should be cached, or inline at the top of function or component to cache the return value. ## Usage -Enable support for the `use cache` directive with the [`useCache`](/docs/app/api-reference/config/next-config-js/useCache) flag in your `next.config.ts` file: +`use cache` is currently an experimental feature. To enable it, add the [`useCache`](/docs/app/api-reference/config/next-config-js/useCache) option to your `next.config.ts` file: ```ts filename="next.config.ts" switcher import type { NextConfig } from 'next' @@ -43,9 +43,9 @@ const nextConfig = { module.exports = nextConfig ``` -Additionally, `use cache` directives are also enabled when the [`dynamicIO`](/docs/app/api-reference/config/next-config-js/dynamicIO) flag is set. +> `use cache` can also be enabled with the [`dynamicIO`](/docs/app/api-reference/config/next-config-js/dynamicIO) option. -Then, you can use the `use cache` directive at the file, component, or function level: +Then, add `use cache` at the file, component, or function level: ```tsx // File level @@ -69,20 +69,72 @@ export async function getData() { } ``` -## 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. -- `use cache` is an experimental Next.js feature, and not a native React feature like [`use client`](/docs/app/api-reference/directives/use-client) or [`use server`](/docs/app/api-reference/directives/use-server). -- Any [serializable](https://react.dev/reference/rsc/use-server#serializable-parameters-and-return-values) arguments (or props) passed to the cached function, as well as any serializable values it reads from the parent scope, will be converted to a format like JSON and automatically become a part of the cache key. -- Any non-serializable arguments, props, or closed-over values will turn into opaque references inside the cached function, and can be only passed through and not inspected nor modified. These non-serializable values will be filled in at the request time and won't become a part of the cache key. - - For example, a cached function can take in JSX as a `children` prop and return `
{children}
`, but it won't be able to introspect the actual `children` object. -- The return value of the cacheable function must also be serializable. This ensures that the cached data can be stored and retrieved correctly. -- 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. -- If used alongside [Partial Prerendering](/docs/app/building-your-application/rendering/partial-prerendering), segments that have `use cache` will be prerendered as part of the static HTML shell. -- Unlike [`unstable_cache`](/docs/app/api-reference/functions/unstable_cache) which only supports JSON data, `use cache` can cache any serializable data React can render, including the render output of components. +## How `use cache` works + +### Cache keys + +`use cache` creates a cache entry, the key of which is generated using the following: + +- Build ID (generated for each build) +- Function ID (a secure identifier unique to the function) +- The [serializable](https://react.dev/reference/rsc/use-server#serializable-parameters-and-return-values) function arguments (or props). + +The arguments passed to the cached function, as well as any values it reads from the parent scope, are converted to a format like JSON and automatically become a part of the key. This means, the same cache entry will be reused as long as the arguments passed to the function are the same. + +## Non-serializable arguments + +Any non-serializable arguments, props, or closed-over values will turn into opaque references inside the cached function, and can be only passed through and not inspected nor modified. These non-serializable values will be filled in at the request time and won't become a part of the cache key. + +For example, a cached function can take in JSX as a `children` prop and return `
{children}
`, but it won't be able to introspect the actual `children` object. + +```tsx filename="app/ui/cached-component.tsx" switcher +function CachedComponent({ children }: { children: ReactNode }) { + 'use cache' + return
{children}
+} +``` + +```jsx filename="app/ui/cached-component.js" switcher +function CachedComponent({ children }) { + 'use cache' + return
{children}
+} +``` + +## Return values + +The return value of the cacheable function must be serializable. This ensures that the cached data can be stored and retrieved correctly. + +## At build time + +When used at the top of a [layout](/docs/app/api-reference/file-conventions/layout) or [page](/docs/app/api-reference/file-conventions/page), the route segment will be prerendered, allowing it to later be [revalidated](#during-revalidation). + +This means `use cache` cannot be used with [request-time APIs](/docs/app/building-your-application/rendering/server-components#dynamic-apis) like `cookies` or `headers`. + +## At runtime + +During runtime, the `use cache` entries will be cached in-memory for the duration of the server request. + +Then, on the client, any content returned from the server will be cached in the browser's memory for the duration of the session or until [revalidated](#during-revalidation). + +## During revalidation + +By default, Next.js sets a revalidation period of **15 minutes** when you use the `use cache` directive, and a near-infinite expiration duration. + +While this revalidation period may be useful for content that doesn't require frequent updates, you can use the `cacheLife` and `cacheTag` APIs to configure when the individual cache entries should be revalidated. + +- [`cacheLife`](/docs/app/api-reference/functions/cacheLife): For time-based revalidation periods. +- [`cacheTag`](/docs/app/api-reference/functions/cacheTag): For on-demand revalidation. + +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. + +See the [`cacheLife`](/docs/app/api-reference/functions/cacheLife) and [`cacheTag`](/docs/app/api-reference/functions/cacheTag) API docs for more information. ## 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 of **both** the `layout` and `page` files. Each of these segments are treated as separate entry points in your application, and will be cached independently. @@ -140,11 +192,9 @@ export default function Page() { > This is recommended for applications that previously used the [`export const dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) option, and will ensure the entire route is prerendered. -### 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. +### Caching a component's output with `use cache` -The props are serialized and form part of the cache key, and the cache entry will be reused as long as the serialized props produce the same value in each instance. +You can use `use cache` at the component level to cache any fetches or computations performed within that component. The cache entry will be reused as long as the serialized props produce the same value in each instance. ```tsx filename="app/components/bookings.tsx" highlight={2} switcher export async function Bookings({ type = 'haircut' }: BookingsProps) { @@ -174,7 +224,7 @@ export async function Bookings({ type = 'haircut' }) { ### 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. +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, a database query, or a slow computation. ```tsx filename="app/actions.ts" highlight={2} switcher export async function getData() { @@ -194,19 +244,6 @@ export async function getData() { } ``` -### Revalidating - -By default, Next.js sets a **[revalidation period](/docs/app/building-your-application/data-fetching/fetching#revalidating-cached-data) of 15 minutes** when you use the `use cache` directive. Next.js sets 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: - -- [`cacheLife`](/docs/app/api-reference/functions/cacheLife): For time-based revalidation periods. -- [`cacheTag`](/docs/app/api-reference/functions/cacheTag): For on-demand revalidation. - -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. - -See the [`cacheLife`](/docs/app/api-reference/functions/cacheLife) and [`cacheTag`](/docs/app/api-reference/functions/cacheTag) docs for more information. - ### Interleaving If you need to pass non-serializable arguments to a cacheable function, you can pass them as `children`. This means the `children` reference can change without affecting the cache entry.