Revalidación
Actualiza datos almacenados en caché bajo demanda o en un temporizador con revalidatePath, revalidateTag e ISR.
Busca en todas las páginas de la documentación
Actualiza datos almacenados en caché bajo demanda o en un temporizador con revalidatePath, revalidateTag e ISR.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Tarjeta de referencia rápida -- lista para copiar y pegar.
// Revalidación basada en tiempo (ISR)
await fetch(url, { next: { revalidate: 60 } }); // revalidar cada 60 segundos
// Obtención basada en etiquetas
await fetch(url, { next: { tags: ["products"] } });
// Revalidación bajo demanda por etiqueta
import { revalidateTag } from "next/cache";
revalidateTag("products");
// Revalidación bajo demanda por ruta
import { revalidatePath } from "next/cache";
revalidatePath("/products"); // revalidar una página específica
revalidatePath("/products", "layout"); // revalidar layout y todas las páginas hijas
revalidatePath("/", "layout"); // revalidar toda la aplicaciónCuándo usarlo: Tienes datos almacenados en caché que necesitan actualizarse -- ya sea periódicamente (ISR) o inmediatamente después de una mutación.
// lib/products.ts
export async function getProducts() {
const res = await fetch("https://api.example.com/products", {
next: { revalidate: 300, tags: ["products"] },
});
if (!res.ok) throw new Error("Failed to fetch products");
return res.json() as Promise<Product[]>;
}
export async function getProduct(slug: string) {
const res = await fetch(`https://api.example.com/products/${slug}`, {
next: { tags: ["products", `product-${slug}`] },
});
if (!res.ok) throw new Error("Product not found");
return res.json() as Promise<Product>;
}// app/actions/product.ts
"use server";
import { revalidateTag, revalidatePath } from "next/cache";
import { db } from "@/lib/db";
export async function updateProduct(formData: FormData) {
const slug = formData.get("slug") as string;
const name = formData.get("name") as string;
const price = Number(formData.get("price"));
await db.product.update({
where: { slug },
data: { name, price },
});
// Revalidar solo los datos en caché de este producto
revalidateTag(`product-${slug}`);
// También revalidar la página de listado de productos
revalidatePath("/products");
}
export async function deleteProduct(slug: string) {
await db.product.delete({ where: { slug } });
// Revalidar todos los cachés relacionados con productos
revalidateTag("products");
}// app/api/revalidate/route.ts -- revalidación disparada por webhook
import { revalidateTag } from "next/cache";
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const secret = request.headers.get("x-revalidation-secret");
if (secret !== process.env.REVALIDATION_SECRET) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { tag } = await request.json();
revalidateTag(tag);
return NextResponse.json({ revalidated: true, tag });
}Lo que esto demuestra:
next.revalidate para actualizar en segundo planonext: { revalidate: N }, Next.js sirve la versión en caché durante N segundos. Después de que vence el tiempo, la siguiente solicitud sigue obteniendo la versión obsoleta (respuesta instantánea), pero dispara una regeneración en segundo plano. Las solicitudes posteriores obtienen la versión fresca. Este es el patrón "stale-while-revalidate".revalidateTag: Cada llamada a fetch puede etiquetarse con next: { tags: ["my-tag"] }. Llamar a revalidateTag("my-tag") purga todas las entradas en caché con esa etiqueta. La siguiente solicitud dispara una obtención fresca.revalidatePath: Purga la caché de ruta completa para una ruta específica. Cuando se llama con un segundo argumento de "layout", también revalida todas las páginas anidadas bajo ese layout.revalidateTag como revalidatePath pueden llamarse dentro de Server Actions, Route Handlers o Middleware.Configuración de revalidación a nivel de segmento:
// app/products/layout.tsx
// Todas las páginas bajo /products se revalidan cada 5 minutos
export const revalidate = 300;Revalidar datos que no son fetch con unstable_cache:
import { unstable_cache } from "next/cache";
import { db } from "@/lib/db";
const getCachedUser = unstable_cache(
async (id: string) => db.user.findUnique({ where: { id } }),
["user-cache"],
{ tags: ["users"], revalidate: 600 }
);Forzar renderización dinámica (optar por no almacenar en caché):
// app/dashboard/page.tsx
export const dynamic = "force-dynamic";// firma de revalidatePath
function revalidatePath(
originalPath: string,
type?: "layout" | "page"
): void;
// firma de revalidateTag
function revalidateTag(tag: string): void;
// tipos de exportación de configuración de segmento
export const revalidate: number | false = 60;
export const dynamic: "auto" | "force-dynamic" | "force-static" | "error" = "auto";revalidatePath no se revalida instantáneamente -- Marca la caché como obsoleta. La respuesta actual sigue sirviendo datos obsoletos. Los datos frescos aparecen en la siguiente solicitud. Solución: Entiende el modelo de stale-while-revalidate; si necesitas datos frescos inmediatos en la respuesta actual, usa cache: "no-store".
Las etiquetas son globales -- Llamar a revalidateTag("data") purga cada entrada en caché etiquetada con "data" en toda la aplicación. Solución: Usa etiquetas específicas con espacios de nombres como "product-abc123" en lugar de genéricas.
revalidatePath("/") no revalida todo -- Solo revalida la página raíz. Solución: Usa revalidatePath("/", "layout") para revalidar el layout raíz y todas las páginas hijas.
ISR revalidate: 0 significa sin caché -- Establecer revalidate en 0 opta por renderización dinámica en la ruta, no por "revalidar inmediatamente". Solución: Usa un entero positivo para ISR; usa cache: "no-store" para datos verdaderamente dinámicos.
Múltiples tiempos de revalidación en la misma ruta -- Si diferentes obtenciones en la misma ruta usan diferentes valores de revalidate, Next.js usa el valor más bajo para toda la ruta. Solución: Sé consistente con intervalos de revalidación dentro de una ruta, o divide en límites Suspense separados.
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
cache: "no-store" | Los datos deben estar frescos en cada solicitud | Puedes tolerar datos ligeramente obsoletos |
| Sondeo del lado cliente (SWR/React Query) | Necesitas actualizaciones en tiempo real en el navegador | La revalidación del lado servidor es suficiente |
Webhook + revalidateTag | Un sistema externo (CMS, pago) dispara purga de caché | Controlas la mutación en tus propias Server Actions |
revalidatePath | Quieres actualizar una página completa independientemente de etiquetas | Necesitas control granular sobre datos específicos |
force-dynamic completo | Cada dato en la ruta debe ser dinámico | Algunas partes de la página pueden ser estáticas |
revalidatePath purga la caché de ruta completa para una ruta de URL específicarevalidateTag purga todas las entradas en caché etiquetadas con una cadena específicacache: "no-store"/), no la aplicación completarevalidatePath("/", "layout")revalidate: 60 y otra usa revalidate: 300, la ruta se revalida cada 60 segundos// app/api/revalidate/route.ts
import { revalidateTag } from "next/cache";
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const secret = request.headers.get("x-revalidation-secret");
if (secret !== process.env.REVALIDATION_SECRET) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { tag } = await request.json();
revalidateTag(tag);
return NextResponse.json({ revalidated: true });
}cache: "no-store"function revalidatePath(
originalPath: string,
type?: "layout" | "page"
): void;"page""layout" para revalidar el layout y todas las páginas anidadasunstable_cache de next/cache y asigna etiquetasrevalidateTag con la misma etiqueta para invalidarimport { unstable_cache } from "next/cache";
const getCachedUser = unstable_cache(
async (id: string) => db.user.findUnique({ where: { id } }),
["user-cache"],
{ tags: ["users"], revalidate: 600 }
);// app/products/layout.tsx
export const revalidate = 300;revalidateTag("data") purga cada entrada en caché con esa etiqueta en toda la aplicación"product-abc123" para evitar purgas de caché no intencionadasRevisado por Chris St. John·Última actualización: 16 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥