Revalidação
Atualize dados em cache sob demanda ou em um temporizador com revalidatePath, revalidateTag e ISR.
Busque em todas as páginas da documentação
Atualize dados em cache sob demanda ou em um temporizador com revalidatePath, revalidateTag e ISR.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Cartão de receita de referência rápida - pronto para copiar e colar.
// Revalidação baseada em tempo (ISR)
await fetch(url, { next: { revalidate: 60 } }); // revalidar a cada 60 segundos
// Busca baseada em tag
await fetch(url, { next: { tags: ["products"] } });
// Revalidação sob demanda por tag
import { revalidateTag } from "next/cache";
revalidateTag("products");
// Revalidação sob demanda por caminho
import { revalidatePath } from "next/cache";
revalidatePath("/products"); // revalidar uma página específica
revalidatePath("/products", "layout"); // revalidar o layout e todas as páginas filhas
revalidatePath("/", "layout"); // revalidar todo o aplicativoQuando usar isso: Você tem dados em cache que precisam ser atualizados - seja periodicamente (ISR) ou imediatamente após uma mutação.
// 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("Falha ao buscar produtos");
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("Produto não encontrado");
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 },
});
// Revalida apenas os dados em cache deste produto
revalidateTag(`product-${slug}`);
// Também revalida a página de listagem de produtos
revalidatePath("/products");
}
export async function deleteProduct(slug: string) {
await db.product.delete({ where: { slug } });
// Revalida todos os caches relacionados a produtos
revalidateTag("products");
}// app/api/revalidate/route.ts -- revalidação acionada 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 });
}O que isso demonstra:
next.revalidate para atualização em segundo planonext: { revalidate: N } é definido, o Next.js serve a versão em cache por N segundos. Após o tempo expirar, a próxima solicitação ainda recebe a versão desatualizada (resposta instantânea), mas dispara uma regeneração em segundo plano. Solicitações subsequentes recebem a versão atualizada. Este é o padrão "stale-while-revalidate" (desatualizado enquanto se revalida).revalidateTag: Cada chamada fetch pode ser marcada com next: { tags: ["my-tag"] }. Chamar revalidateTag("my-tag") limpa todas as entradas de cache com essa tag. A próxima solicitação dispara uma nova busca.revalidatePath: Limpa o cache completo da rota para um caminho específico. Quando chamado com um segundo argumento "layout", ele também revalida todas as páginas aninhadas sob esse layout.revalidateTag quanto revalidatePath podem ser chamados dentro de Server Actions, Route Handlers ou Middleware.Configuração de revalidação em nível de segmento:
// app/products/layout.tsx
// Todas as páginas sob /products revalidam a cada 5 minutos
export const revalidate = 300;Revalidar dados não-fetch com 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 }
);Forçar renderização dinâmica (optar por fora de todo o cache):
// app/dashboard/page.tsx
export const dynamic = "force-dynamic";// assinatura de revalidatePath
function revalidatePath(
originalPath: string,
type?: "layout" | "page"
): void;
// assinatura de revalidateTag
function revalidateTag(tag: string): void;
// tipos de exportação de configuração de segmento
export const revalidate: number | false = 60;
export const dynamic: "auto" | "force-dynamic" | "force-static" | "error" = "auto";revalidatePath não revalida instantaneamente - Ele marca o cache como desatualizado. A resposta atual ainda serve dados desatualizados. Os dados atualizados aparecem na próxima solicitação. Correção: Entenda o modelo stale-while-revalidate; se você precisar de frescor instantâneo na resposta atual, use cache: "no-store".
Tags são globais - Chamar revalidateTag("data") limpa todas as entradas de cache com a tag "data" em todo o aplicativo. Correção: Use tags específicas e com namespace, como "product-abc123", em vez de genéricas.
revalidatePath("/") não revalida tudo - Ele apenas revalida a página raiz. Correção: Use revalidatePath("/", "layout") para revalidar o layout raiz e todas as páginas filhas.
ISR revalidate: 0 significa sem cache - Definir revalidate como 0 opta a rota para renderização dinâmica, não "revalidar imediatamente". Correção: Use um inteiro positivo para ISR; use cache: "no-store" para dados verdadeiramente dinâmicos.
Múltiplos tempos de revalidação na mesma rota - Se diferentes buscas na mesma rota usarem valores revalidate diferentes, o Next.js usará o menor valor para toda a rota. Correção: Seja consistente com os intervalos de revalidação dentro de uma rota, ou divida em limites de Suspense separados.
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
cache: "no-store" | Dados devem estar frescos em cada solicitação | Você pode tolerar dados ligeiramente desatualizados |
| Polling do lado do cliente (SWR/React Query) | Você precisa de atualizações em tempo real no navegador | A revalidação do lado do servidor é suficiente |
Webhook + revalidateTag | Sistema externo (CMS, pagamento) aciona a limpeza do cache | Você controla a mutação em suas próprias Server Actions |
revalidatePath | Você deseja atualizar uma página inteira, independentemente das tags | Você precisa de controle granular sobre dados específicos |
force-dynamic completo | Cada dado na rota deve ser dinâmico | Algumas partes da página podem ser estáticas |
revalidatePath limpa o cache completo da rota para um caminho de URL específicorevalidateTag limpa todas as entradas de cache marcadas com uma string específicacache: "no-store"/), não o aplicativo inteirorevalidatePath("/", "layout")revalidate: 60 e outra usa revalidate: 300, a rota revalida a 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 o layout e todas as páginas aninhadasunstable_cache de next/cache e atribua tagsrevalidateTag com a mesma tag 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") limpa todas as entradas de cache com essa tag em todo o aplicativo"product-abc123" para evitar limpezas de cache não intencionaisRevisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥