Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

In Next.js applications using the Cache Components model, enable cacheComponents, put 'use cache' around work that is safe to reuse, and choose a freshness profile with cacheLife. Add tags for shared data that mutations must invalidate; use updateTag when a Server Action needs an immediate update, revalidateTag when stale-while-revalidate is acceptable, and revalidatePath when the route is the target. Read request-specific values outside the cached scope and pass only the values the cached work needs.

These instructions describe Cache Components, not every Next.js application. The Next.js documentation maintains a separate guide for the previous caching model. Check your installed Next.js version and configuration before adopting either set of APIs; the documentation cited here was updated in February and March 2026.

How do I enable and use the Cache Components model?

Cache Components must be enabled before using the 'use cache', cacheLife, and related patterns described here. In a TypeScript configuration file, the setting looks like this:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  cacheComponents: true,
}

export default nextConfig

Place 'use cache' at the top of a route, component, or function whose result can be reused. A data function is often the clearest boundary: it makes the cached work explicit and gives you one place to set freshness and invalidation behavior.

import { cacheLife, cacheTag } from 'next/cache'

async function getProducts() {
  'use cache'
  cacheLife('hours')
  cacheTag('products')

  return db.product.findMany()
}

The built-in default cache profile documents five minutes of client stale time and fifteen minutes until server revalidation, with no time-based expiration. Choose a profile deliberately rather than assuming that every cached value has one simple time-to-live.

How should I think about cache freshness?

cacheLife separates three timing questions. In a custom profile, its values are expressed in seconds:

  • stale: how long the client router may use cached data without contacting the server.
  • revalidate: how often the server refreshes the cached result.
  • expire: the maximum time stale content can remain before a request must wait for fresh content.

For example, this profile uses illustrative values of 60, 300, and 3600 seconds; they are not universal recommendations:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
cacheLife({
  stale: 60,
  revalidate: 300,
  expire: 3600,
})

Set these values according to the data’s acceptable staleness and the experience the product requires. A short client stale interval does not itself mean the server refreshes every minute, and a revalidation interval is not the same as a hard expiration.

How do I invalidate cached data after a mutation?

Choose the invalidation API based on both scope and user experience. Tags identify cached data by relationship; paths target a route. The current API reference documents these alternatives:

API Target Update behavior
updateTag(tag) Cached entries carrying the tag Use in a Server Action when the user should see the updated data immediately.
revalidateTag(tag, 'max') Cached entries carrying the tag Marks the tagged data stale; the documented 'max' profile uses stale-while-revalidate behavior.
revalidatePath(path) A route path Revalidates data associated with the specified path.

For instance, a Server Action that updates a product can invalidate the shared product data after the write succeeds:

'use server'

import { updateTag } from 'next/cache'

export async function renameProduct(id: string, name: string) {
  await db.product.update({ where: { id }, data: { name } })
  updateTag('products')
}

Use revalidateTag('products', 'max') instead if briefly serving stale content while fresh data is obtained is acceptable. Use revalidatePath('/products') when the route itself is the intended invalidation target. A tag is useful when multiple cached functions or views depend on the same data; a path is useful when the route is the unit you want to refresh. The one-argument form revalidateTag(tag) is deprecated; follow the current API signature rather than copying older examples.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do I cache data that depends on cookies or headers?

Request-specific APIs such as cookies and headers belong outside a cached scope. Read the needed value first, then pass it as an argument to the cached function:

import { cookies } from 'next/headers'

async function getAccountView(userId: string) {
  'use cache'
  return db.account.findFirst({ where: { userId } })
}

export default async function AccountPage() {
  const cookieStore = await cookies()
  const userId = cookieStore.get('user-id')?.value

  if (!userId) return <p>Sign in to view your account.</p>

  const account = await getAccountView(userId)
  return <AccountDetails account={account} />
}

Arguments contribute to distinct cached results, so a user identifier can separate results for different users. That does not automatically make arbitrary personalized output safe to cache: pass the correct identity or other relevant scope, and ensure the returned content is safe to reuse for requests with the same cache inputs. Do not read a request value inside the cached function and assume the cache will distinguish users for you.

How do I use caching in a Route Handler?

Do not put 'use cache' directly in a Route Handler body. Put the cache directive in a helper that the handler calls, then return its result using the normal response API:

import { cacheLife, cacheTag } from 'next/cache'

async function getCatalog() {
  'use cache'
  cacheLife('hours')
  cacheTag('catalog')
  return db.product.findMany()
}

export async function GET() {
  const catalog = await getCatalog()
  return Response.json(catalog)
}

When a new request arrives, the cached helper’s data follows its configured cacheLife behavior. Keep request-dependent logic in the handler or pass the required values into a helper that can safely distinguish its cached results.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

When is a remote cache handler worth considering?

The 'use cache: remote' directive uses a platform-provided cache handler. It may suit deployments where in-memory runtime caching is insufficient, but it introduces network round trips and may involve platform charges. Evaluate it against the application’s workload and deployment topology, including whether instances need shared cached data. The documentation does not establish which provider is fastest, least expensive, or most reliable, so those choices require platform-specific evaluation rather than an assumed performance win.

How does this differ from the previous Next.js caching model?

Next.js documents a separate caching guide for applications that do not use Cache Components. Do not mix its examples or assumptions with the model above.

  • In the previous model, the extended server fetch API has persistent Data Cache semantics. cache: 'force-cache' consults the Data Cache, and next.revalidate sets a maximum cache lifetime.
  • Conflicting fetch options such as cache: 'no-store' together with a positive next.revalidate value are not allowed.
  • The previous-model guide covers explicit fetch caching and unstable_cache for non-fetch functions. Those examples are not substitutes for Cache Components APIs in an application using that model.

Cache Components require the Node.js runtime; the documentation says not to use them with the Edge Runtime. Confirm the supported configuration against the Next.js version installed in your project.

How should I choose a pattern?

  • Use a cached function or component when its output is safe to reuse and its inputs define the right cache boundary.
  • Use cacheLife to express client stale time, server refresh frequency, and the point when a request must wait for fresh content.
  • Use tags for shared data relationships and paths for route-level invalidation.
  • Use immediate invalidation after a mutation when the flow requires fresh data at once; use stale-while-revalidate when a short delay is acceptable.
  • Consider a remote handler only after accounting for shared-cache needs, network cost, and platform fees.

Cache profiles and APIs describe behavior, not a guaranteed speedup. Measure the application under its actual request mix and deployment conditions before making performance claims.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.