Revalidating cached data

Next.js · Advanced · 13 min

Caching is only safe if you can update it the moment data changes.

Cached data needs a way to become fresh again. Next.js offers two strategies.

Time-based: the cacheLife profile decides when entries go stale and are regenerated in the background (stale-while-revalidate), and when they expire completely.

On-demand, after a mutation:

  • `updateTag(tag)` expires tagged entries immediately so the user who made the change sees it on the next render (read-your-own-writes). It can only be called in Server Actions.
  • `revalidateTag(tag, profile)` marks entries stale with stale-while-revalidate semantics: visitors may get the old version once while a fresh one is generated. In Next.js 16 the second argument is required; 'max' is recommended. Usable in Server Actions and Route Handlers, e.g. a CMS webhook.
  • `revalidatePath('/blog')` invalidates everything for a path (add 'layout' to include nested routes). Simple, but coarser than tags.
  • `refresh()` re-renders the current page's uncached server data from a Server Action; it doesn't touch tagged caches.

Choosing: user edits their own profile, use updateTag in the action. Editor publishes in a CMS, the webhook route calls revalidateTag('posts', 'max'). Tag precisely (post:42 as well as posts) so you can invalidate narrowly.

All caches are scoped to one deployment: a new deploy starts with fresh prerenders.

Example

// app/posts/actions.ts
'use server'
import { updateTag } from 'next/cache'
import { db } from '@/lib/db'

export async function renamePost(id: string, title: string) {
  await db.post.update({ where: { id }, data: { title } })
  updateTag(`post:${id}`)        // the author sees the new title immediately
  updateTag('posts')
}

// app/api/cms-webhook/route.ts
import { 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 })
  }
  const { postId } = await request.json()
  revalidateTag(`post:${postId}`, 'max')   // serve stale once, regenerate in background
  revalidateTag('posts', 'max')
  return Response.json({ revalidated: true })
}

Remember

  • cacheLife handles time-based freshness
  • updateTag (Server Actions only) for read-your-own-writes; revalidateTag(tag, 'max') for background refresh
  • revalidatePath invalidates by route; refresh() re-renders uncached data

Watch out for

  • Calling revalidateTag('posts') without the second argument, which Next.js 16 requires.

Check yourself

  1. A user edits their profile in a Server Action and must see the change immediately. Which call fits best?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: nextjs.org/docs/app/getting-started/revalidating