Revalidation strategies in Next.js
What is Revalidation?
Revalidation is the process of updating cached data in Next.js. It allows you to keep statically generated pages fresh without rebuilding the entire site. Next.js offers multiple revalidation strategies.
Time-Based Revalidation
Automatically revalidate after a specified number of seconds:
// Using fetch with revalidate
async function getProducts() {
const res = await fetch("https://api.example.com/products", {
next: { revalidate: 3600 }, // Revalidate every 1 hour
});
return res.json();
}
// Or at the page/layout level
export const revalidate = 3600; // Revalidate this page every 1 hourHow Time-Based Revalidation Works
1. First request → generates page, serves cached version
2. Before 1 hour → serves cached version (fast!)
3. After 1 hour → first request still serves stale cache
→ triggers background regeneration
4. Next request → serves freshly generated pageThis is called stale-while-revalidate (SWR) pattern.
On-Demand Revalidation
Revalidate immediately when data changes:
revalidatePath
// app/api/webhook/route.ts
import { revalidatePath } from "next/cache";
export async function POST(request: Request) {
const data = await request.json();
// Update database...
await updateProduct(data);
// Revalidate specific path
revalidatePath("/products");
// Revalidate dynamic path
revalidatePath("/products/" + data.slug);
// Revalidate layout (all pages using it)
revalidatePath("/products", "layout");
return Response.json({ revalidated: true });
}revalidateTag
// Tag your fetch requests
async function getProducts() {
const res = await fetch("https://api.example.com/products", {
next: { tags: ["products"] },
});
return res.json();
}
async function getProduct(id: string) {
const res = await fetch(`https://api.example.com/products/${id}`, {
next: { tags: ["products", `product-${id}`] },
});
return res.json();
}
// Revalidate all fetches tagged with "products"
import { revalidateTag } from "next/cache";
export async function POST() {
revalidateTag("products"); // All product-related caches refreshed
return Response.json({ revalidated: true });
}Server Actions with Revalidation
"use server";
import { revalidatePath, revalidateTag } from "next/cache";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
// Save to database
await db.insert(posts).values({ title });
// Revalidate
revalidateTag("posts");
revalidatePath("/blog");
}// Client component using the Server Action
"use client";
export function CreatePostForm() {
return (
<form action={createPost}>
<input name="title" placeholder="Post title" />
<button type="submit">Create</button>
</form>
);
}Comparison
| Strategy | When to use | Freshness |
|---|---|---|
revalidate: false (or no option) | Data never changes | Static forever |
revalidate: N (seconds) | Data changes periodically | Fresh within N seconds |
revalidatePath() | After a specific mutation | Immediate |
revalidateTag() | After related data changes | Immediate, surgical |
cache: "no-store" | Always need latest data | Real-time (no cache) |
Opt-Out of Caching
// No caching at all — always fetch fresh data
async function getLatestPrices() {
const res = await fetch("https://api.example.com/prices", {
cache: "no-store",
});
return res.json();
}
// Or at page level
export const dynamic = "force-dynamic";Important:
Use time-based revalidation for data that changes periodically (e.g., blog posts, product listings). Use on-demand revalidation (revalidatePath/revalidateTag) when you know exactly when data changes (e.g., after form submissions, webhook events). The combination gives you static performance with dynamic freshness.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.