NextjsRevalidation (Time & On-Demand)

Revalidation (Time & On-Demand)

Revalidation is the process of telling Next.js that cached data (and any static HTML built from it) is no longer fresh and should be regenerated. There are two complementary ways to trigger it: on a fixed time interval, or on demand at the exact moment you know data changed.

Time-based revalidation, recap

Time-based revalidation is what powers ISR: you attach a revalidate window, in seconds, to a fetch call or a whole route segment, and Next.js treats the cached result as stale once that window elapses, regenerating it in the background on the next request.

TSX
// Per fetch call
await fetch('https://api.example.com/posts', { next: { revalidate: 3600 } })

// Per route segment
export const revalidate = 3600

This is simple and effective as a baseline, but it is inherently imprecise — data might change one second after a regeneration just happened, and visitors would still see the old version for up to another full window.

On-demand revalidation: the precise alternative

On-demand revalidation lets you invalidate exactly the right cached data at exactly the right moment, instead of guessing at a time window. Next.js provides two functions for this, both imported from next/cache and typically called from inside a Server Action or a Route Handler right after a mutation succeeds.

TSX
'use server'

import { revalidatePath, revalidateTag } from 'next/cache'

export async function publishPost(slug: string) {
  await savePostToDatabase(slug)

  // Invalidate one specific path...
  revalidatePath(`/blog/${slug}`)

  // ...or invalidate every fetch anywhere tagged 'posts'
  revalidateTag('posts')
}
Note
revalidatePath and revalidateTag each have their own dedicated page in this section with fuller examples. This page focuses on when to reach for time-based vs. on-demand revalidation, and how the two fit together.
Time-based vs. on-demand

Approach

Precision

Effort to wire up

Best for

Time-based (revalidate: N)

Data can be stale for up to N seconds

One config value

Content you don’t control the update moment for (external APIs, CMS webhooks you haven’t wired up yet)

On-demand (revalidatePath / revalidateTag)

Immediate — stale for effectively zero time

Requires calling it from the mutation code path

Content updated through your own app (Server Actions, admin panels, CMS webhooks)

Combining both approaches

These two mechanisms are not mutually exclusive, and most production apps use both together. A sensible default is a moderately long time-based revalidate window as a safety net, paired with on-demand revalidation for the paths you know exactly when to invalidate.

TSX
// Fetch: cached for up to an hour, but also invalidatable early by tag
await fetch('https://api.example.com/posts', {
  next: { revalidate: 3600, tags: ['posts'] },
})

// Server Action: called right after an edit, invalidates immediately
'use server'
import { revalidateTag } from 'next/cache'

export async function editPost() {
  await updatePostInDatabase()
  revalidateTag('posts') // don't wait for the hour-long window
}
Tip
Think of the time-based window as protecting you against data sources you don't fully control (a flaky third-party API, a CMS without webhooks), and on-demand revalidation as the fast path for anything mutated through your own Server Actions or Route Handlers.
Warning
On-demand revalidation only clears the cache — it does not itself re-fetch data. The actual regeneration happens lazily, the next time that path or tag is requested, so the very first request after revalidating pays the cost of rebuilding it.