Caching and Revalidating (Previous Model)
Learn how to cache and revalidate data using fetch options, unstable_cache, and route segment configs for projects not using Cache Components.
This guide assumes you are not using Cache Components which was introduced in version 16 under the cacheComponents flag.
Caching fetch requests
By default, fetch requests are not cached. You can cache individual requests by setting the cache option to 'force-cache'.
See the fetch API reference to learn more.
unstable_cache for non-fetch functions
unstable_cache allows you to cache the result of database queries and other async functions that don't use fetch. Wrap unstable_cache around the function:
The third argument accepts:
tags: an array of tags for on-demand revalidation withrevalidateTag.revalidate: the number of seconds before the cache is revalidated.
See the unstable_cache API reference to learn more.
Route segment config
You can configure caching behavior at the route level by exporting config options from a Page, Layout, or Route Handler.
dynamic
Change the dynamic behavior of a layout or page to fully static or fully dynamic.
'auto'(default): The default option to cache as much as possible without preventing any components from opting into dynamic behavior.'force-dynamic': Force dynamic rendering, which will result in routes being rendered for each user at request time. This option is equivalent to:- Setting the option of every
fetch()request in a layout or page to{ cache: 'no-store', next: { revalidate: 0 } }. - Setting the segment config to
export const fetchCache = 'force-no-store'
- Setting the option of every
'error': Force prerendering and cache the data of a layout or page by causing an error if any components use Request-time APIs or uncached data. This option is equivalent to:getStaticProps()in thepagesdirectory.- Setting the option of every
fetch()request in a layout or page to{ cache: 'force-cache' }. - Setting the segment config to
fetchCache = 'only-cache'.
'force-static': Force prerendering and cache the data of a layout or page by forcingcookies,headers()anduseSearchParams()to return empty values. It is possible torevalidate,revalidatePath, orrevalidateTag, in pages or layouts rendered withforce-static.
fetchCache
This is an advanced option that should only be used if you specifically need to override the default behavior.
By default, Next.js will cache any fetch() requests that are reachable before any Request-time APIs are used and will not cache fetch requests that are discovered after Request-time APIs are used.
fetchCache allows you to override the default cache option of all fetch requests in a layout or page.
'auto'(default): The default option to cachefetchrequests before Request-time APIs with thecacheoption they provide and not cachefetchrequests after Request-time APIs.'default-cache': Allow anycacheoption to be passed tofetchbut if no option is provided then set thecacheoption to'force-cache'. This means that evenfetchrequests after Request-time APIs are considered static.'only-cache': Ensure allfetchrequests opt into caching by changing the default tocache: 'force-cache'if no option is provided and causing an error if anyfetchrequests usecache: 'no-store'.'force-cache': Ensure allfetchrequests opt into caching by setting thecacheoption of allfetchrequests to'force-cache'.'default-no-store': Allow anycacheoption to be passed tofetchbut if no option is provided then set thecacheoption to'no-store'. This means that evenfetchrequests before Request-time APIs are considered dynamic.'only-no-store': Ensure allfetchrequests opt out of caching by changing the default tocache: 'no-store'if no option is provided and causing an error if anyfetchrequests usecache: 'force-cache''force-no-store': Ensure allfetchrequests opt out of caching by setting thecacheoption of allfetchrequests to'no-store'. This forces allfetchrequests to be re-fetched every request even if they provide a'force-cache'option.
Cross-route segment behavior
- Any options set across each layout and page of a single route need to be compatible with each other.
- If both the
'only-cache'and'force-cache'are provided, then'force-cache'wins. If both'only-no-store'and'force-no-store'are provided, then'force-no-store'wins. The force option changes the behavior across the route so a single segment with'force-*'would prevent any errors caused by'only-*'. - The intention of the
'only-*'and'force-*'options is to guarantee the whole route is either fully static or fully dynamic. This means:- A combination of
'only-cache'and'only-no-store'in a single route is not allowed. - A combination of
'force-cache'and'force-no-store'in a single route is not allowed.
- A combination of
- A parent cannot provide
'default-no-store'if a child provides'auto'or'*-cache'since that could make the same fetch have different behavior.
- If both the
- It is generally recommended to leave shared parent layouts as
'auto'and customize the options where child segments diverge.
Time-based revalidation
Use the next.revalidate option on fetch to revalidate data after a specified number of seconds:
For non-fetch functions, unstable_cache accepts a revalidate option in its configuration (see example above).
Route segment config revalidate
Set the default revalidation time for a layout or page. This option does not override the revalidate value set by individual fetch requests.
false(default): The default heuristic to cache anyfetchrequests that set theircacheoption to'force-cache'or are discovered before a Request-time API is used. Semantically equivalent torevalidate: Infinitywhich effectively means the resource should be cached indefinitely. It is still possible for individualfetchrequests to usecache: 'no-store'orrevalidate: 0to avoid being cached and make the route dynamically rendered. Or setrevalidateto a positive number lower than the route default to increase the revalidation frequency of a route.0: Ensure a layout or page is always dynamically rendered even if no Request-time APIs or uncached data fetches are discovered. This option changes the default offetchrequests that do not set acacheoption to'no-store'but leavesfetchrequests that opt into'force-cache'or use a positiverevalidateas is.number: (in seconds) Set the default revalidation frequency of a layout or page tonseconds.
Good to know:
- The revalidate value needs to be statically analyzable. For example
revalidate = 600is valid, butrevalidate = 60 * 10is not. - The revalidate value is not available when using
runtime = 'edge'. - In Development, Pages are always rendered on-demand and are never cached. This allows you to see changes immediately without waiting for a revalidation period to pass.
Revalidation frequency
- The lowest
revalidateacross each layout and page of a single route will determine the revalidation frequency of the entire route. This ensures that child pages are revalidated as frequently as their parent layouts. - Individual
fetchrequests can set a lowerrevalidatethan the route's defaultrevalidateto increase the revalidation frequency of the entire route. This allows you to dynamically opt-in to more frequent revalidation for certain routes based on some criteria.
On-demand revalidation
To revalidate cached data after an event, use revalidateTag or revalidatePath in a Server Action or Route Handler.
Tagging cached data
Tag fetch requests with next.tags to enable on-demand cache invalidation:
For non-fetch functions, unstable_cache also accepts a tags option (see example above).
revalidateTag
Invalidate cached data by tag using revalidateTag:
revalidatePath
Invalidate all cached data for a specific route path using revalidatePath:
Deduplicating requests
If you are not using fetch (which is automatically memoized), and instead using an ORM or database directly, you can wrap your data access with the React cache function to deduplicate requests within a single render pass:
Preloading data
You can preload data by creating a utility function that you eagerly call above blocking requests. This lets you initiate data fetching early, so the data is already available by the time the component renders.
Combine the server-only package with React's cache to create a reusable preload utility:
Then call preload() before any blocking work so the data starts loading immediately: