Skip to main content
Practice Problems

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:

tsx
// 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 hour

How 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 page

This is called stale-while-revalidate (SWR) pattern.

On-Demand Revalidation

Revalidate immediately when data changes:

revalidatePath

tsx
// 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

tsx
// 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

tsx
"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"); }
tsx
// 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

StrategyWhen to useFreshness
revalidate: false (or no option)Data never changesStatic forever
revalidate: N (seconds)Data changes periodicallyFresh within N seconds
revalidatePath()After a specific mutationImmediate
revalidateTag()After related data changesImmediate, surgical
cache: "no-store"Always need latest dataReal-time (no cache)

Opt-Out of Caching

tsx
// 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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems