Next.js cookbook · Advanced

Revalidate cached data with tags after a mutation

Show fresh product data right after an admin edits it, while keeping pages fast and cached.

Approach

Tag cached data (fetch(..., { next: { tags: ["products"] } }) or 'use cache' + cacheTag() with Cache Components) and call revalidateTag() (or revalidatePath()) in the Server Action after the write. In Next.js 16, revalidateTag takes a cache-life profile as a second argument, and updateTag() gives read-your-own-writes inside Server Actions; check the docs for your version.

Solution

// lib/products.ts
export async function getProduct(id: string) {
  const res = await fetch(`${process.env.API_URL}/products/${id}`, {
    next: { tags: ["products", `product:${id}`], revalidate: 3600 },
  });
  if (!res.ok) throw new Error("Failed to load product");
  return (await res.json()) as Product;
}

// app/admin/products/[id]/actions.ts
"use server";
import { revalidatePath, revalidateTag } from "next/cache";

export async function updatePrice(id: string, formData: FormData) {
  const session = await verifyAdmin();
  const price = Number(formData.get("price"));
  if (!Number.isInteger(price) || price < 0) return { error: "Invalid price" };
  await api.patch(`/products/${id}`, { price }, session.token);

  revalidateTag(`product:${id}`, "max");      // Next.js 16 signature; v15: revalidateTag(`product:${id}`)
  revalidatePath("/products");                // listing page
  return { ok: true };
}

Pitfalls

  • Revalidating everything (revalidatePath('/', 'layout')) on every write.
  • Forgetting that revalidation is per deployment/cache — external caches (CDN) may need purging too.
  • Mixing cache semantics across Next.js versions without checking the docs.

Variations

  • Time-based ISR (revalidate) for data that can be slightly stale.
  • Cache Components ('use cache', cacheLife, cacheTag) in Next.js 16.

Further reading: Next.js: revalidateTag

Reference: https://nextjs.org/docs/app/api-reference/functions/revalidateTag

More Next.js recipes