Skip to content
C.W.K.
Stream
Lesson 04 of 09 · published

On-Demand Revalidation

~22 min · revalidatePath, revalidateTag, Server Actions

Level 0Curious
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

Why on-demand

Time-based ISR means users might see stale data for the whole revalidate window. On-demand revalidation lets you invalidate the cache the instant a mutation lands — so editors who change content see the new version immediately.

Two functions, two scopes

FunctionInvalidates
revalidatePath(path, type?)A specific path. type can be 'page' (just the leaf) or 'layout' (everything under).
revalidateTag(tag)Every fetch tagged with tag via fetch(url, { next: { tags } }).

Where you can call them

Server Actions and Route Handlers only. v15 made this strict; calling them during render throws. The reason: invalidation is a side effect that belongs to mutations, not to pure rendering.

Model invalidation from the data outward. Updating one post can affect its detail view, the listing, and an author page. Stable tags such as post:<id> plus a collection tag often express that dependency more safely than a growing list of paths. One global tag makes implementation easy but destroys most of the cache on every change. Extremely narrow ID tags can leave aggregates stale. The useful middle is a small vocabulary aligned with actual data relationships.

Render the same record in three locations and compare path and tag invalidation after create, update, and delete. Invalidation must happen only after a successful write, and a render-time call should remain a failing regression test.

Code

Invalidate a path after a mutation·tsx
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { db } from '@/lib/db';

export async function publishPost(formData: FormData) {
  const title = formData.get('title') as string;
  const slug = await db.post.create({ data: { title } });

  revalidatePath('/blog');                     // listing
  revalidatePath(`/blog/${slug}`, 'page');     // specific post
  revalidatePath('/blog', 'layout');           // everything under /blog
}
Tag-based invalidation·tsx
// In the fetch:
const posts = await fetch('https://api.example.com/posts', {
  next: { tags: ['posts'] },
}).then(r => r.json());

// In the action:
import { revalidateTag } from 'next/cache';

export async function createPost(formData: FormData) {
  'use server';
  await db.post.create({ data: {/* … */} });
  revalidateTag('posts'); // every fetch tagged 'posts' is invalidated
}

External links

Exercise

Add a Server Action that updates a post and calls revalidatePath. Confirm the listing page shows the new value on the very next request without waiting for any time-based interval.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.