Type something to search...
Next.js cacheTag

Next.js cacheTag

Caching data is only half the problem — the other half is invalidating exactly the right subset of it when something changes, without wiping out everything else you'd cached. cacheTag is how you label a cached entry so it can be targeted for invalidation later, by name, rather than needing to know its cache key or wait out its natural expiration. This is the deep API reference; the "Caching" article elsewhere on this blog covers the getting-started-level basics of how tagging fits into the broader model.

Prerequisite

const nextConfig: NextConfig = {
  cacheComponents: true,
};

Basic Usage

import { cacheTag } from "next/cache";

export async function getData() {
  "use cache";
  cacheTag("my-data");
  const res = await fetch("https://api.example.com/data");
  const data = await res.json();
  return data;
}

cacheTag accepts one or more string values, applied inside a use cache scope. Once tagged, that cache entry can be purged on demand from a Server Function or Route Handler — you don't have to wait for its cacheLife-configured expiration to naturally arrive.

Two Invalidation Functions, Two Different Guarantees

This is the detail worth getting right before reaching for either function, since picking the wrong one produces subtly wrong UX rather than an obvious error:

  • updateTag — for read-your-own-writes scenarios: a form submission, any user-triggered mutation where the very next read needs to reflect the change immediately. updateTag is only available inside Server Functions — it's specifically scoped to the "user just did something, now show them the result" pattern.
  • revalidateTag — for cases where serving briefly stale data while revalidation happens in the background is genuinely acceptable, or for invalidating from a Route Handler or other context outside a Server Function.
"use server";
import { updateTag } from "next/cache";

export default async function submit() {
  await addPost();
  updateTag("my-data");
}

Choosing updateTag here specifically guarantees the user sees their own change reflected on the very next read — the stronger, more immediate guarantee revalidateTag's background-refresh model doesn't make on its own.

Behavioral Rules Worth Knowing

Tags are idempotent. Applying the same tag to the same entry multiple times has no additional effect beyond the first application — there's no "double-tagging" concern to worry about.

Multiple tags per entry are fully supported, just by passing multiple arguments:

cacheTag("tag-one", "tag-two");

There are hard limits worth knowing before you build a tagging scheme around fine granularity: a single cacheTag() call accepts up to 128 tags, each with a maximum length of 256 characters. Exceed either limit and Next.js doesn't error loudly — it silently skips the offending tag (over the length limit) or drops anything past the 128th tag in that one call, logging a console warning in both cases rather than failing the build or the request. If you're building a tagging scheme programmatically (say, generating one tag per related entity ID), it's worth being deliberate about staying under these ceilings rather than discovering a dropped tag via a console warning you might not be watching for.

Tagging From Inside the Function Body vs. at the Top

You can tag a cache entry either right at the top of the cached scope (a static, known-ahead-of-time tag name):

export async function Bookings({ type = "haircut" }: BookingsProps) {
  "use cache";
  cacheTag("bookings-data");
  // ...
}

Or derive the tag from data the function itself fetches — genuinely useful when the meaningful cache key isn't known until after the fetch resolves (an ID assigned by the backend, for instance):

async function getBookingsData() {
  "use cache";
  const response = await fetch(
    `https://api.example.com/bookings?type=${encodeURIComponent(type)}`,
  );
  const data = await response.json();
  cacheTag("bookings-data", data.id);
  return data;
}

This second pattern — tagging with both a broad category tag (bookings-data) and a specific instance tag (data.id) in the same call — is a genuinely useful combination: it lets you invalidate either "everything booking-related" or "just this one specific booking" later, without having to choose one granularity upfront.

Invalidating What You Tagged

"use server";
import { revalidateTag } from "next/cache";

export async function updateBookings() {
  await updateBookingData();
  revalidateTag("bookings-data", "max");
}

The second argument to revalidateTag here ('max') is worth noting as a real, meaningful parameter rather than a stray value — consult the revalidateTag reference directly for its full semantics, since its exact behavior around cache profile interaction is specific enough to warrant its own dedicated treatment rather than a passing mention here.

Key Takeaways

AspectDetail
RequiresCache Components enabled, and usage inside a use cache scope
Tag applicationIdempotent — repeated tagging has no additional effect
Multiple tagsPass multiple string arguments to one cacheTag() call
Limits128 tags per call, 256 characters per tag — violations are silently dropped with a console warning, not a hard error
updateTagServer Functions only — guarantees the very next read reflects the change (read-your-own-writes)
revalidateTagBroader context (Route Handlers included) — acceptable when briefly-stale reads are fine
Tagging strategyCombine a broad category tag with a specific instance tag for flexible invalidation granularity later

cacheTag is the labeling half of a two-part system — tag now, invalidate precisely later, via whichever of updateTag or revalidateTag actually matches the freshness guarantee your specific mutation needs. Get the tag granularity right upfront (broad plus specific, where it makes sense), and invalidation becomes a matter of picking the right tag name rather than reasoning about cache keys directly.

Tags :
Share :

Related Posts

Can Next.js Be Used with GraphQL?

Can Next.js Be Used with GraphQL?

Next.js and GraphQL are two powerful technologies that have gained significant traction in the web development community. Next.js, a React-based fram

Dive Deeper
How does Next.js differ from Create React App?

How does Next.js differ from Create React App?

In the world of modern web development, React.js has emerged as a dominant force due to its flexibility, performance, and extensive ecosystem. Two po

Dive Deeper
How does Next.js handle image optimization?

How does Next.js handle image optimization?

In modern web development, image optimization plays a critical role in enhancing user experience and improving site performance. Large, unoptimized i

Dive Deeper