Next.js path · Rendering and caching · Topic 5 of 11

Revalidation: by time, by tag, by path

Not started

Revalidation is how cached data gets replaced. Next.js offers two strategies, which you usually combine. **Time-based** revalidation comes from cacheLife: after revalidate seconds the next request triggers a background refresh. **On-demand** revalidation is something you call after a change: tag cached data with cacheTag, then invalidate the tag with revalidateTag or updateTag, or invalidate a whole route with revalidatePath. The choice between them is mostly about one question: may the next visitor briefly see the old version?

Why it matters: Caching is easy; knowing when cached content is wrong is the hard part. Next.js gives you a clock and three ways to say "this changed", and they differ in whether the next visitor waits.

  1. 1Time-based

    `cacheLife` alone: entries are refreshed in the background once `revalidate` passes. Good when slightly old data is fine and changes have no event you can hook into.

  2. 2Tag, then invalidate

    `cacheTag("posts")` labels entries. `revalidateTag("posts", "max")` marks them stale everywhere; visitors get the old version while a new one is made.

  3. 3Read your own writes

    `updateTag` expires the tag immediately, so the user who just saved sees the change. It works only inside Server Actions.

  4. 4Paths as a fallback

    `revalidatePath("/blog")` invalidates what a route uses when you don't know its tags. Tags are more precise and invalidate less.

1

How it works

The explanation, with examples you can run.

Tagging and invalidating

// lib/posts.tsimport { cacheLife, cacheTag } from "next/cache" export async function getPosts() {  "use cache"  cacheLife("max")          // no reason to refresh on a clock  cacheTag("posts")  return db.post.findMany({ orderBy: { createdAt: "desc" } })}
// app/api/cms-webhook/route.ts: called by the CMS when content changesimport { revalidateTag } from "next/cache" export async function POST(request: Request) {  if (request.headers.get("x-webhook-secret") !== process.env.CMS_SECRET) {    return new Response("Forbidden", { status: 403 })  }  revalidateTag("posts", "max")  return Response.json({ revalidated: true })}
// app/posts/actions.ts"use server"import { updateTag } from "next/cache"import { redirect } from "next/navigation" export async function createPost(formData: FormData) {  const user = await requireUser()                 // actions are public endpoints  const post = await db.post.create({ data: { title: String(formData.get("title")), authorId: user.id } })  updateTag("posts")  redirect(`/posts/${post.id}`)}

For content that changes on an event (a CMS publish, an admin edit), a long cacheLife plus a tag is better than a short clock: pages stay in the static shell and refresh only when something actually changed.

Stale-while-revalidate or wait

revalidateTag(tag, profile)updateTag(tag)revalidatePath(path)
Called fromServer Actions, Route HandlersServer Actions onlyServer Actions, Route Handlers
Next requestserved stale while refreshing (with "max")waits for fresh datainvalidates the route's cached data
Use forbackground updates, webhooksthe user's own changewhen you don't know the tags

The second argument to revalidateTag is required in Next.js 16: it says how long stale content may still be served. "max" is the recommended choice; { expire: 0 } means never serve stale. The old one-argument form is deprecated.

Two related tools: refresh() in a Server Action re-renders the current page for the client without touching any cache, and revalidatePath with a dynamic pattern ("/blog/[slug]") needs a type, "page" or "layout", to say how much of the tree to invalidate.

None of these purge a CDN that sits in front of your server under its own rules; that is part of how you deploy, covered later in this path.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.A CMS webhook calls your Route Handler when an article is published. Which call fits?
  2. 2.A user edits their profile name in a form handled by a Server Action, and the redirected page must show the new name. Which call fits?
  3. 3.What does calling `revalidateTag('posts', 'max')` do by itself, before anyone visits?

0 of 3 right so far. Get all three to finish this topic.