There are two ways of caching in Next.js: Segment-level and Per-Request Cache.
Segment-level caching allows you to cache and revalidate data used in route segments.
This mechanism allows different segments of a path to control the cache lifetime of a route. Each page.tsx
and layout.tsx
in the route hierarchy can export a revalidate
value that sets the revalidation time for the route.
export const revalidate = 60; // revalidate this page every 60 seconds
In addition to the exported revalidate
value, requests made using fetch
can specify a revalidate
option to control the revalidation frequency of the route.
// revalidate this page every 10 seconds, since the getData's fetch
// request has `revalidate: 10`.
async function getData() {
const res = await fetch('https://...', { next: { revalidate: 10 } });
return res.json();
}
export default async function Page() {
const data = await getData();
// ...
}
If a page, layout, and fetch request all specify a revalidation frequency then the lowest value of the three will be used.
export const revalidate = 60;
export default function Layout() {
// ...
}
export const revalidate = 30;
async function getData() {
const res = await fetch('https://...', { next: { revalidate: 10 } });
return res.json();
}
export default async function Page() {
const data = await getData();
// ...
}
In this example the layout, page, and fetch request are all asking for the route to be revalidated at different times.
Since the fetch request has the shortest revalidation time, this page will use its value and be revalidated every 10 seconds.
See revalidate
for other possible configuration options.
Good to know:
fetchCache
to 'only-cache'
or 'force-cache'
to ensure that all fetch
requests opt into caching but the revalidation frequency might still be lowered by individual fetch
requests. See fetchCache
for more information.Per-request caching allows you to cache and deduplicate requests on a per-request basis.
React exposes a new function, cache()
, that memoizes the result of a wrapped function. The same function called with the same arguments will reuse a cached value instead of re-running the function. This cache()
function was introduced in the first-class support for promises RFC.
You can use cache()
to deduplicate data fetches on a per-request basis. If a function instance with the same arguments has been called before, anywhere in the server request, then we can return a cached value.
import { cache } from 'react';
export const getUser = cache(async (id: string) => {
const user = await db.user.findUnique({ id });
return user;
});
import { getUser } from '@utils/getUser';
export default async function UserLayout({ params: { id } }) {
const user = await getUser(id);
// ...
}
import { getUser } from '@utils/getUser';
export default async function UserLayout({
params: { id },
}: {
params: { id: string };
}) {
const user = await getUser(id);
// ...
}
Although the getUser()
function is called twice in the example above, only one query will be made to the database. This is because getUser()
is wrapped in cache()
, so the second request can reuse the result from the first request.
Good to know:
fetch()
has already been patched to include support for cache()
automatically, so you don't need to wrap functions that use fetch()
with cache()
. See automatic request deduping for more information.cache
for the same per-request caching behavior.cache
allows shared data fetching without having to know if a parent layout was rendered during this request.server-only
package to make sure server data fetching functions are not accidentally used on the client.cache()
POST
requests are not automatically deduplicated when using fetch
– only GET
requests are. If you are using GraphQL and POST
requests, you can use cache
to deduplicate requests. The cache
arguments must be flat and only include primitives. Deep objects won’t match for deduplication.
import { cache } from 'react';
export const getUser = cache(async (id: string) => {
const res = await fetch('/graphql', { method: 'POST', body: '...' })
// ...
});
cache()
As a pattern, we suggest optionally exposing a preload()
export in utilities or components that do data fetching.
import { getUser } from "@utils/getUser";
export const preload = (id: string) => {
// void evaluates the given expression and returns undefined
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
void getUser(id);
}
export default async function User({ id }: { id: string }) {
const result = await getUser(id);
// ...
}
By calling preload
, you can eagerly start fetching data you're likely going to need.
import User, { preload } from '@components/User';
export default async function Page({
params: { id },
}: {
params: { id: string };
}) {
preload(id); // starting loading the user data now
const condition = await fetchCondition();
return condition ? <User id={id} /> : null;
}
Good to know:
- The
preload()
function can have any name. It's a pattern, not an API.- This pattern is completely optional and something you can use to optimize on a case-by-case basis. This pattern is a further optimization on top of parallel data fetching. Now you don't have to pass promises down as props and can instead rely on the preload pattern.
cache
, preload
, and server-only
You can combine the cache
function, the preload
pattern, and the server-only
package to create a data fetching utility that can be used throughout your app.
import { cache } from 'react';
import 'server-only';
export const preload = (id: string) => {
void getUser(id);
}
export const getUser = cache(async (id: string) => {
// ...
});
With this approach, you can eagerly fetch data, cache responses, and guarantee that this data fetching only happens on the server.
The getUser.ts
exports can be used by layouts, pages, or components to give them control over when a user's data is fetched.