Cache no Next.js: guia completo para usar revalidate no momento certo

Ali estava o “dado antigo” na tela, depois da vigésima atualização. Dez minutos antes, eu havia alterado manualmente o título no banco de dados, mas a página simplesmente não atualizava. Abri o código: revalidate: 60 estava declarado com todas as letras. Irritado, apaguei e troquei por revalidate: 10. Reiniciei o servidor. Atualizei a página. O dado antigo continuava lá.
O sistema de cache talvez seja a parte mais enlouquecedora do Next.js. São quatro camadas, três métodos de revalidação e ainda uma mudança incompatível da versão 14 para a 15. Você acha que definir revalidate: 60 resolve tudo? Talvez o Router Cache esteja atrapalhando, talvez o Full Route Cache não tenha sido invalidado ou talvez você esteja testando no ambiente de desenvolvimento.
Este artigo explica da forma mais direta possível:
- Quantas camadas de cache o Next.js esconde e o que cada uma controla
- Qual é a diferença entre revalidatePath, revalidateTag e updateTag, e quando usar cada um
- Como investigar cada camada quando os dados não são atualizados
Se você também já encontrou dados desatualizados, um revalidate que parece não funcionar ou dúvidas sobre qual API usar, os próximos 12 minutos podem poupar várias noites em claro.
Por que o cache do Next.js é tão complexo
Por que existem tantas camadas?
Sinceramente, quando vi pela primeira vez que o Next.js tinha quatro camadas de cache, provavelmente fiz a mesma cara que você está fazendo agora. Request Memoization? Full Route Cache? O que é tudo isso? Não poderia ser mais simples?
Pensando com calma, porém, cada camada resolve um problema de desempenho em um contexto diferente:
- Se dez componentes da sua árvore precisam dos dados do usuário, não faz sentido enviar dez solicitações iguais.
- Se a lista de posts do seu blog muda poucas vezes por dia, não faz sentido renderizá-la de novo a cada acesso.
- Se o usuário aperta o botão Voltar, não faz sentido obrigá-lo a esperar a página carregar novamente.
Cada cache tem sua responsabilidade. O problema é que eles afetam uns aos outros. Por isso, depois de alterar os dados, nem sempre fica claro qual cache deve ser invalidado.
Next.js 14 versus 15: uma revolução no cache
No fim de 2024, o Next.js 15 trouxe uma mudança importante: solicitações feitas com fetch deixaram de ser armazenadas em cache por padrão.
Antes (14):
fetch(url) // Cache por padrão, equivalente a cache: 'force-cache'
Agora (15):
fetch(url) // Sem cache por padrão, equivalente a cache: 'no-store'
Depois da atualização, muita gente viu o desempenho despencar porque dados antes armazenados automaticamente passaram a ser buscados em todas as solicitações. Houve muitas reclamações no fórum da Vercel, mas a justificativa oficial foi: “O explícito é melhor que o implícito; o cache deve ser uma escolha consciente do desenvolvedor, não um comportamento padrão.”
Faz sentido. Para projetos existentes, porém, continua sendo uma mudança incompatível.
Visão geral das quatro camadas
De forma simples, os dados atravessam estas quatro camadas entre o servidor e o navegador:
-
Request Memoization (memorização de solicitações)
Escopo: ciclo de renderização de uma única solicitação
Responsável: React -
Data Cache (cache de dados)
Escopo: servidor, persistente entre solicitações
Responsável: Next.js -
Full Route Cache (cache completo da rota)
Escopo: servidor, rotas estáticas
Responsável: Next.js -
Router Cache (cache do roteador)
Escopo: memória do navegador no cliente
Responsável: Next.js
O fluxo se parece com isto:
Acesso do usuário → Router Cache (cliente) → Full Route Cache (servidor)
↓
Data Cache → Request Memoization → fonte de dados
Seu revalidate afeta principalmente o Data Cache e o Full Route Cache. Para limpar o Router Cache, é preciso chamar router.refresh() ou fazer uma atualização forçada.
É por isso que, às vezes, mesmo depois da revalidação, o cliente ainda exibe dados antigos: o Router Cache continua ativo.
Na próxima seção, vamos examinar as quatro camadas, entender o que cada uma faz e quando é criada ou invalidada.
As quatro camadas de cache em detalhes
Request Memoization (memorização de solicitações)
O que é isso?
Na verdade, esse é um recurso do React 18, não algo criado especificamente pelo Next.js. Em resumo, se vários componentes fazem a mesma solicitação GET durante um ciclo de renderização, o React as combina automaticamente em uma só.
Veja um exemplo:
// app/page.tsx
async function UserProfile() {
const user = await fetch('https://api.example.com/user/123')
return <div>{user.name}</div>
}
async function UserAvatar() {
const user = await fetch('https://api.example.com/user/123') // Mesma solicitação
return <img src={user.avatar} />
}
export default function Page() {
return (
<>
<UserProfile />
<UserAvatar />
</>
)
}
Os dois componentes solicitam a mesma URL, mas apenas uma solicitação é enviada. O React memoriza o primeiro resultado e o reutiliza na segunda chamada.
Ciclo de vida muito curto
Esse cache vale apenas durante uma renderização. Quando ela termina, o cache é descartado. Na próxima atualização da página, começa uma nova solicitação.
Pontos de atenção
- Funciona apenas em Server Components.
- Funciona apenas para solicitações GET; POST e PUT não são memorizadas.
- O efeito pode não ficar evidente no ambiente de desenvolvimento, pois cada alteração no código dispara uma nova renderização.
Quando isso é útil?
Talvez você nem precise se preocupar com essa camada. É uma otimização automática do React e não pode ser controlada manualmente. Ela aparece aqui para deixar claro que repetir o mesmo fetch em vários componentes não significa necessariamente enviar várias solicitações.
Data Cache (cache de dados)
Este é o ponto central
O Data Cache é o núcleo do cache do Next.js. Ele armazena os resultados das solicitações fetch no sistema de arquivos do servidor, de forma persistente entre solicitações, usuários e implantações.
A enorme diferença entre Next.js 14 e 15
Next.js 14:
fetch('https://api.example.com/posts')
// Equivalente a
fetch('https://api.example.com/posts', { cache: 'force-cache' })
// Resultado: os dados ficam armazenados até uma revalidação manual
Next.js 15:
fetch('https://api.example.com/posts')
// Equivalente a
fetch('https://api.example.com/posts', { cache: 'no-store' })
// Resultado: uma nova solicitação é feita sempre; não há cache
Para armazenar dados em cache no Next.js 15
Opção 1: configurar uma solicitação específica
fetch('https://api.example.com/posts', {
cache: 'force-cache',
next: { revalidate: 3600 } // Revalida após uma hora
})
Opção 2: configurar a rota inteira
// app/blog/page.tsx
export const revalidate = 3600
export default async function BlogPage() {
const posts = await fetch('https://api.example.com/posts')
// ...
}
Quando ele é invalidado?
O Data Cache pode ser invalidado de três formas:
- O intervalo definido em
revalidatetermina, por exemplo, após 3.600 segundos. revalidatePath()ourevalidateTag()é chamado manualmente.- O aplicativo é implantado novamente.
E se vários fetch tiverem intervalos de revalidação diferentes?
Se uma página tem vários fetch com intervalos distintos, o Next.js usa o menor deles como intervalo de revalidação da página inteira.
async function Page() {
const posts = await fetch('...', { next: { revalidate: 60 } }) // 60 segundos
const user = await fetch('...', { next: { revalidate: 3600 } }) // Uma hora
// Na prática, a página inteira será revalidada a cada 60 segundos
}
Full Route Cache (cache completo da rota)
Cache no nível do HTML
Se o Data Cache armazena os dados, o Full Route Cache armazena o HTML completo da página e o RSC Payload, isto é, os dados serializados dos React Server Components.
Quando uma rota é armazenada?
Somente rotas com renderização estática entram nesse cache. Uma rota é estática quando seu conteúdo pode ser determinado durante o build.
Por outro lado, uma página passa a usar renderização dinâmica e não é armazenada se utilizar:
cookies()headers()searchParams- Funções instáveis, como
Math.random()ouDate.now()
Como saber se a página é estática ou dinâmica?
Execute npm run build. O terminal exibirá algo semelhante a:
Route (app) Size First Load JS
┌ ○ / 5 kB 87 kB
├ ● /blog 1 kB 88 kB
└ ƒ /api/user 0 kB 87 kB
○ (Static) Renderizado automaticamente como HTML estático, sem dados dinâmicos
● (SSG) Gerado automaticamente como HTML estático + JSON, com getStaticProps
ƒ (Dynamic) Renderizado sob demanda no servidor
Os símbolos ○ e ● indicam uma rota estática, que será armazenada em cache. O símbolo ƒ indica uma rota dinâmica, sem cache.
Como forçar o modo estático ou dinâmico
// Força renderização estática
export const dynamic = 'force-static'
// Força renderização dinâmica
export const dynamic = 'force-dynamic'
Quando ele é invalidado?
O Full Route Cache pode ser invalidado quando:
- O Data Cache é invalidado. Como os dados mudaram, a página precisa ser renderizada novamente.
revalidatePath('/blog')é chamado.- O aplicativo é implantado novamente.
Router Cache (cache do roteador)
Um truque no cliente
O Router Cache fica na memória do navegador. Depois que o usuário visita uma página, o Next.js armazena seu conteúdo no cliente. Ao voltar ou navegar novamente até ela, usa esse conteúdo sem consultar o servidor.
Há mais um truque: a pré-busca
Se você usa <Link href="/about">, quando o link entra na área visível da tela, o Next.js faz a pré-busca automática do conteúdo de /about e o armazena no Router Cache. Quando o usuário clica, a navegação é instantânea.
Ciclo de vida no Next.js 14
- Rotas estáticas: 5 minutos
- Rotas dinâmicas: 30 segundos
Mudanças no Next.js 15
Por padrão, o Next.js 15 não ativa o Router Cache, ou usa um intervalo muito curto. Para ativá-lo, configure next.config.js:
// next.config.js
module.exports = {
experimental: {
staleTimes: {
dynamic: 30, // Cache de rota dinâmica por 30 segundos
static: 180, // Cache de rota estática por 180 segundos
},
},
}
Quando ele é invalidado?
- Quando o intervalo de cache termina.
- Quando o usuário faz uma atualização forçada com Ctrl+Shift+R.
- Quando
router.refresh()é chamado.
Por que revalidate parece não funcionar?
Muitas vezes, revalidatePath foi chamado no servidor e os dados foram atualizados, mas o usuário ainda vê a versão antiga ao atualizar a página. Quase sempre, o motivo é que o Router Cache ainda está válido.
Soluções:
- O usuário faz uma atualização forçada, embora você não possa exigir isso dele.
- Depois de atualizar os dados, chame
router.refresh()em um Client Component. - Reduza o intervalo do Router Cache.
Todos os métodos de revalidação
Agora que as quatro camadas estão claras, chegamos à parte mais prática: como invalidar o cache.
O Next.js oferece vários métodos de revalidação, cada um adequado a uma situação. Entender as diferenças economiza muito tempo de depuração.
Revalidação baseada em tempo
A opção mais comum
A revalidação baseada em tempo busca os dados novamente, de forma automática, a cada X segundos. Ela é o mecanismo central do ISR, ou Incremental Static Regeneration.
Duas formas de configurar
Opção 1: no fetch
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 } // 3.600 segundos = 1 hora
})
Opção 2: no nível superior do arquivo da rota
// app/blog/page.tsx
export const revalidate = 3600
export default async function BlogPage() {
const posts = await fetch('https://api.example.com/posts')
return <PostList posts={posts} />
}
Como o ISR funciona
Imagine que você definiu revalidate: 3600:
- O primeiro usuário acessa a página: um HTML estático é gerado e armazenado por uma hora.
- Durante essa hora, todos recebem o mesmo HTML em cache, com muita rapidez.
- Depois de uma hora, o próximo usuário acessa: ele ainda recebe o HTML antigo, sem esperar.
- Ao mesmo tempo, o Next.js gera um novo HTML em segundo plano.
- Quando esse HTML fica pronto, os acessos seguintes recebem o conteúdo novo.
Esse mecanismo é chamado de stale-while-revalidate, ou seja, conteúdo antigo é entregue durante a revalidação. A vantagem é que o usuário não espera; a desvantagem é que um usuário ainda verá dados desatualizados.
Quando usar
- Lista de posts atualizada algumas vezes por hora.
- Página inicial de notícias atualizada a cada 30 minutos.
- Catálogo de produtos atualizado uma vez por dia.
Problema comum 1: não funciona em desenvolvimento
Muita gente configura revalidate e conclui que ele não funciona. A primeira pergunta deve ser: o teste está sendo feito em desenvolvimento ou produção?
Em desenvolvimento, com npm run dev, o Next.js desativa grande parte dos caches e renderiza de novo a cada solicitação. Teste no ambiente de produção:
npm run build
npm start
Problema comum 2: vários fetch com intervalos diferentes
Como vimos, se uma página tem vários fetch com intervalos distintos, o Next.js usa o menor. Há, porém, um detalhe: o Data Cache ainda respeita o intervalo individual de cada fetch.
async function Page() {
// Esta solicitação é revalidada a cada 60 segundos
const posts = await fetch('...', { next: { revalidate: 60 } })
// Esta solicitação é revalidada a cada 3.600 segundos
const user = await fetch('...', { next: { revalidate: 3600 } })
}
A página será renderizada novamente a cada 60 segundos, mas os dados de user continuarão armazenados durante uma hora. Nesse período, a página muda a cada minuto, porém os dados do usuário permanecem iguais; somente após uma hora eles serão atualizados.
Pode parecer confuso, mas é um comportamento coerente.
Revalidação sob demanda com revalidatePath
Atualizações disparadas pelo usuário
A revalidação baseada em tempo funciona como um temporizador. Já revalidatePath funciona como um botão: quando um evento específico acontece, como a publicação de um post, você invalida o cache manualmente.
Como usar
// app/actions.ts
'use server'
import { revalidatePath } from 'next/cache'
export async function publishPost(formData) {
// Lógica de publicação do post
await db.posts.create({ ... })
// Invalida o cache da lista do blog
revalidatePath('/blog')
}
Depois, chame a função em um Client Component:
// app/components/PublishButton.tsx
'use client'
import { publishPost } from '@/app/actions'
export function PublishButton() {
return (
<form action={publishPost}>
<button type="submit">Publicar post</button>
</form>
)
}
Tipos de caminho
revalidatePath aceita um tipo de caminho:
// Revalida apenas a página /blog
revalidatePath('/blog', 'page')
// Revalida todas as páginas sob /blog, incluindo /blog/post-1 e /blog/post-2
revalidatePath('/blog', 'layout')
Importante: a página não é gerada imediatamente
Muita gente acredita que a página é gerada assim que revalidatePath é chamado. Não é assim.
revalidatePath apenas marca o cache como inválido. A nova geração só acontece quando o próximo usuário acessa a página.
Na prática:
- Você chama
revalidatePath('/blog'). - O cache é limpo.
- O próximo usuário acessa
/blog: só então o HTML é gerado novamente, e ele precisa esperar. - Os acessos seguintes recebem o conteúdo novo.
Quando usar
- Atualizar uma lista depois que o usuário publica conteúdo.
- Atualizar páginas relacionadas depois que um administrador muda uma configuração.
- Atualizar a página atual depois do envio de um formulário.
Revalidação sob demanda com revalidateTag
Controle de invalidação mais flexível
revalidatePath invalida por caminho; revalidateTag invalida por tag. Você pode associar tags aos dados e depois invalidar de uma vez todo o cache que usa determinada tag.
Como usar
Etapa 1: associe tags aos fetch
const posts = await fetch('https://api.example.com/posts', {
next: {
revalidate: 3600,
tags: ['posts'] // Adiciona a tag 'posts'
}
})
const authors = await fetch('https://api.example.com/authors', {
next: {
revalidate: 3600,
tags: ['posts', 'authors'] // Uma solicitação pode ter várias tags
}
})
Etapa 2: invalide o cache de uma tag
'use server'
import { revalidateTag } from 'next/cache'
export async function publishPost() {
await db.posts.create({ ... })
// Todo dado marcado com 'posts' será invalidado
revalidateTag('posts')
}
Estratégia stale-while-revalidate com profile=“max”
No Next.js 15, recomenda-se usar profile="max":
revalidateTag('posts', { profile: 'max' })
Com profile="max":
- O dado é marcado como expirado, mas não é removido imediatamente.
- No próximo acesso, o dado antigo é devolvido rapidamente.
- Ao mesmo tempo, os dados novos são buscados em segundo plano.
- Quando ficam prontos, passam a ser usados nas solicitações seguintes.
É melhor que o comportamento padrão porque o usuário não precisa esperar.
Diferenças entre revalidatePath e revalidateTag
| Critério | revalidatePath | revalidateTag |
|---|---|---|
| Granularidade | Por caminho | Por tag de dados |
| Várias páginas | Limitado aos caminhos informados | Pode atravessar várias páginas |
| Precisão | Menor | Maior |
| Complexidade | Simples | Exige planejar as tags |
Quando usar tags?
Quando os mesmos dados aparecem em várias páginas, tags são mais adequadas.
Imagine um blog com:
- Lista de posts em
/blog. - Página de post em
/blog/[slug]. - Página de autor em
/author/[id]. - Seção de posts recentes na página inicial.
As quatro páginas usam os mesmos dados de posts. Com revalidatePath, seria necessário fazer isto:
revalidatePath('/blog')
revalidatePath('/blog/[slug]')
revalidatePath('/author/[id]')
revalidatePath('/')
Com a tag posts, basta:
revalidateTag('posts')
Todas as páginas que usam essa tag serão invalidadas de uma vez.
Novo: updateTag no Next.js 15
Invalidação imediata em vez de adiada
updateTag é uma API nova do Next.js 15. Ao contrário de revalidateTag, ela remove o cache imediatamente, em vez de apenas marcá-lo como expirado.
'use server'
import { updateTag } from 'next/cache'
export async function updateUserProfile(userId, newData) {
await db.users.update({ where: { id: userId }, data: newData })
// Invalida imediatamente o cache dos dados do usuário
updateTag(`user-${userId}`)
}
Diferenças em relação a revalidateTag
| Critério | revalidateTag | updateTag |
|---|---|---|
| Forma de invalidação | Marca como expirado e atualiza em segundo plano no próximo acesso | Remove o cache imediatamente |
| Próximo acesso | Entrega dados antigos e busca os novos em segundo plano | Aguarda a busca dos dados novos |
| Limitação | Pode ser usado em vários contextos | Só pode ser usado em Server Actions |
| Cenário | Casos gerais, priorizando velocidade | Cenários de leitura após a própria gravação |
O que significa ler a própria gravação?
Imagine que o usuário alterou o apelido em sua página de perfil. Depois de clicar em Salvar, a página deve mostrar imediatamente o novo apelido, não o valor antigo enquanto espera uma atualização em segundo plano.
Nesse caso, use updateTag:
export async function updateProfile(formData) {
const userId = getCurrentUserId()
await db.users.update({
where: { id: userId },
data: { nickname: formData.get('nickname') }
})
// Invalida imediatamente para que a próxima leitura veja os dados mais recentes
updateTag(`user-${userId}`)
revalidatePath('/profile')
}
Novo: diretiva use cache no Next.js 15
Declaração explícita de cache
O Next.js 15 introduziu a diretiva 'use cache', que permite marcar explicitamente as funções cujos resultados devem ser armazenados.
Como usar
'use cache'
export async function getPopularPosts() {
const posts = await db.posts.findMany({
orderBy: { views: 'desc' },
take: 10
})
return posts
}
Com cacheTag
import { unstable_cacheTag as cacheTag } from 'next/cache'
'use cache'
export async function getPostsByAuthor(authorId) {
cacheTag('posts', `author-${authorId}`)
return await db.posts.findMany({
where: { authorId }
})
}
Depois, o cache pode ser invalidado com revalidateTag:
revalidateTag(`author-${authorId}`)
Por que isso é necessário?
No Next.js 15, como fetch não usa cache por padrão, é preciso declarar essa intenção se você não quiser consultar o banco de dados a cada solicitação. use cache torna essa intenção explícita.
Guia para diagnosticar problemas comuns
Com a teoria concluída, chegamos à parte mais útil: como investigar problemas de cache.
A seguir estão quatro problemas frequentes, cada um com etapas de diagnóstico e soluções.
Problema 1: revalidate foi configurado, mas não funciona
Sintoma
Você definiu export const revalidate = 60, mas dez minutos depois a página ainda mostra dados antigos.
Etapas de diagnóstico
1. Confirme que o teste está sendo feito em produção
Esse é o erro mais comum. O ambiente de desenvolvimento, iniciado com npm run dev, desativa grande parte dos caches.
Teste desta forma:
npm run build
npm start
2. Confira se a rota usa renderização dinâmica
Execute npm run build e observe a saída:
Route (app) Size
├ ○ /blog 1 kB ← Estática, usa cache
└ ƒ /profile 2 kB ← Dinâmica, não usa cache
Se a rota estiver marcada com ƒ, revalidate não terá efeito porque rotas dinâmicas não são armazenadas em cache.
Código que pode tornar uma rota dinâmica:
// Estes recursos tornam a rota dinâmica
import { cookies } from 'next/headers'
import { headers } from 'next/headers'
export default function Page({ searchParams }) { // Usa searchParams
const cookieStore = cookies() // Usa cookies
// ...
}
Soluções:
- Se a renderização dinâmica não for necessária, remova esses recursos.
- Se ela for necessária, não espere que
revalidatefuncione; use revalidação sob demanda.
3. Confira a versão do Next.js
O comportamento padrão é diferente nas versões 14 e 15. Se você acabou de atualizar para a 15, muitos dados que antes eram armazenados deixaram de ser.
Solução no Next.js 15:
// Ativa o cache explicitamente
fetch(url, {
cache: 'force-cache',
next: { revalidate: 60 }
})
// Ou usa a diretiva use cache
'use cache'
export async function getData() {
// ...
}
4. Confira se o Router Cache está interferindo
Mesmo que os dados do servidor tenham sido atualizados, o Router Cache pode continuar com a versão antiga no cliente.
Soluções:
- Faça uma atualização forçada com Ctrl+Shift+R.
- No Next.js 15, configure
staleTimescom intervalos menores.
Problema 2: os dados mudaram, mas a página ainda mostra a versão antiga
Sintoma
Você alterou os dados diretamente no banco ou chamou revalidatePath, mas a página continua exibindo a versão antiga.
Estratégia: verifique as quatro camadas, uma por uma
Camada 1: Router Cache no cliente
O cache do cliente é o mais fácil de esquecer.
Teste rápido:
- Faça uma atualização forçada com Ctrl+Shift+R.
- Se os dados forem atualizados, o problema está no Router Cache.
Solução:
'use client'
import { useRouter } from 'next/navigation'
export function RefreshButton() {
const router = useRouter()
return (
<button onClick={() => router.refresh()}>
Atualizar
</button>
)
}
Ou configure intervalos menores em next.config.js:
module.exports = {
experimental: {
staleTimes: {
dynamic: 0, // Desativa o cache de rotas dinâmicas
static: 30, // Cache de rotas estáticas por 30 segundos
},
},
}
Camada 2: Full Route Cache no servidor
Confira se a rota é estática. Nesse caso, o HTML inteiro está armazenado.
Teste rápido:
# Abra a página em uma nova janela anônima
# Se os dados ainda forem antigos, o cache está no servidor
Solução:
'use server'
import { revalidatePath } from 'next/cache'
export async function updateData() {
await db.update({ ... })
// Invalida o cache da rota
revalidatePath('/your-page')
}
Camada 3: Data Cache no servidor
Confira a configuração do fetch.
Teste rápido:
Adicione ao fetch um log com a data e hora:
const data = await fetch(url)
console.log('Fetched at:', new Date().toISOString())
Atualize a página. Se o horário não mudar, os dados vieram do cache.
Soluções:
Opção 1: associe uma tag aos dados e depois a invalide
// Ao buscar os dados
const data = await fetch(url, {
next: { tags: ['my-data'] }
})
// Ao atualizar os dados
revalidateTag('my-data')
Opção 2: desative o cache temporariamente para testar
const data = await fetch(url, {
cache: 'no-store' // Sem cache
})
Camada 4: Request Memoization no servidor
Essa camada raramente causa problemas porque dura apenas uma solicitação. Se as três anteriores estão corretas, provavelmente a causa não é o cache, mas a própria fonte de dados.
Problema 3: devo usar revalidatePath ou revalidateTag?
Árvore de decisão
Você precisa invalidar o cache
|
├─ A mudança afeta apenas uma página
| → Use revalidatePath('/specific-page')
|
├─ A mudança afeta todas as páginas sob um caminho
| → Use revalidatePath('/blog', 'layout')
|
├─ Os dados aparecem em páginas com caminhos diferentes
| → Use revalidateTag('your-tag')
|
└─ A invalidação precisa ser imediata após uma edição do usuário
→ Use updateTag('your-tag') no Next.js 15
Exemplo real: um blog
Imagine que o sistema tem estas páginas:
- Página inicial, com os três posts mais recentes.
- Lista em
/blog, com todos os posts. - Detalhes em
/blog/[slug], com um único post. - Página
/author/[id], com todos os posts de um autor.
Estratégia com tags:
// Associa tags ao buscar os dados
async function getPosts() {
return fetch('https://api.example.com/posts', {
next: {
revalidate: 3600,
tags: ['posts'] // Todos os dados de posts recebem esta tag
}
})
}
async function getPostBySlug(slug) {
return fetch(`https://api.example.com/posts/${slug}`, {
next: {
revalidate: 3600,
tags: ['posts', `post-${slug}`] // Tag geral e tag específica
}
})
}
async function getPostsByAuthor(authorId) {
return fetch(`https://api.example.com/posts?author=${authorId}`, {
next: {
revalidate: 3600,
tags: ['posts', `author-${authorId}-posts`]
}
})
}
Ao publicar um post:
export async function publishPost(formData) {
await db.posts.create({ ... })
// Basta invalidar a tag 'posts'
// Todas as páginas que usam essa tag serão atualizadas
revalidateTag('posts')
}
Ao alterar um post específico:
export async function updatePost(slug, newData) {
await db.posts.update({ where: { slug }, data: newData })
// Invalida apenas o cache relacionado a este post
revalidateTag(`post-${slug}`)
// Ou, se também quiser atualizar as listagens
revalidateTag('posts')
}
Problema 4: o cache deixou de funcionar após migrar do Next.js 14 para o 15
Sintoma
Depois da atualização, dados que antes ficavam armazenados passaram a ser solicitados novamente a cada acesso, e o desempenho caiu muito.
Causa
O Next.js 15 mudou três comportamentos padrão:
- fetch passou de
force-cacheparano-store. - Route Handlers GET deixaram de usar cache por padrão.
- O Router Cache deixou de ser ativado por padrão.
Plano de migração
Opção 1: ative o cache explicitamente, recomendada
// Antes, no Next.js 14
const data = await fetch(url)
// Agora, no Next.js 15: declaração explícita
const data = await fetch(url, {
cache: 'force-cache',
next: { revalidate: 3600 }
})
Opção 2: use a diretiva use cache
'use cache'
export async function getPostList() {
const posts = await db.posts.findMany()
return posts
}
Opção 3: ative o Router Cache
// next.config.js
module.exports = {
experimental: {
staleTimes: {
dynamic: 30,
static: 180,
},
},
}
Comparação antes e depois da migração:
// Next.js 14: cache implícito
export default async function BlogPage() {
const posts = await fetch('https://api.example.com/posts')
// Armazenado automaticamente
}
// Next.js 15: cache explícito
'use cache' // Adicione esta linha
export default async function BlogPage() {
const posts = await fetch('https://api.example.com/posts', {
cache: 'force-cache', // Ou adicione esta opção
next: { revalidate: 3600 }
})
}
Minha recomendação:
Não espere uma migração automática perfeita. Revise cada acesso a dados e decida conscientemente o que precisa ou não de cache. Dá trabalho, mas é mais saudável no longo prazo: você saberá exatamente quais dados estão armazenados e quais não estão.
Boas práticas e estratégia de escolha
Depois de toda essa explicação, podemos resumir quando usar cada estratégia de cache.
Fluxograma para escolher a estratégia de cache
Com que frequência seus dados mudam?
|
├─ Quase nunca, como páginas Sobre e documentação de ajuda
| → Geração estática, sem revalidate
| → Faça uma nova implantação quando houver mudanças
|
├─ Periodicamente, por exemplo a cada hora ou dia
| → ISR + revalidação baseada em tempo
| → export const revalidate = 3600
|
├─ Sem periodicidade, como conteúdo publicado por usuários
| → Revalidação sob demanda
| → revalidatePath ou revalidateTag
|
└─ Em tempo real, como chats e painéis ao vivo
→ Renderização dinâmica + cache: 'no-store'
→ Não use cache
Estratégia para nomear tags
Se você optar por revalidateTag, vale seguir uma convenção como esta:
Níveis de granularidade
-
Baixa granularidade, para invalidação em lote:
posts: todos os posts.products: todos os produtos.users: todos os usuários.
-
Granularidade média, por categoria ou estado:
posts:published: posts publicados.posts:draft: rascunhos.products:category:electronics: produtos eletrônicos.
-
Alta granularidade, para um recurso específico:
post:id:123: post de ID 123.user:profile:456: perfil do usuário de ID 456.
Convenção recomendada
Use namespaces no formato entity:type:id:
// Ao buscar os dados
const post = await fetch(`/api/posts/${id}`, {
next: {
tags: [
'posts', // Baixa granularidade
'posts:published', // Granularidade média
`post:id:${id}` // Alta granularidade
]
}
})
// Escolha a granularidade na hora de invalidar
revalidateTag('posts') // Invalida todos os posts
revalidateTag('posts:published') // Invalida apenas os publicados
revalidateTag(`post:id:${id}`) // Invalida um post específico
Recomendações de desempenho
1. Não exagere no cache
Mais cache nem sempre é melhor. O excesso pode causar:
- Dados inconsistentes.
- Depuração mais difícil.
- Desperdício de armazenamento.
Regra prática:
- Dados personalizados do usuário, como carrinho e preferências → não armazene.
- Dados públicos, como listas de produtos e posts → armazene.
- Dados em tempo real, como estoque e usuários online → não armazene ou use um intervalo muito curto.
2. Escolha um intervalo razoável de revalidate
Não use intervalos curtos demais. Definir revalidate: 1 significa que a página pode ser gerada outra vez a cada segundo, anulando os benefícios do cache.
Valores recomendados:
- Notícias: 30 a 60 minutos.
- Posts de blog: 1 a 2 horas.
- Catálogo de produtos: 2 a 4 horas.
- Páginas estáticas: 24 horas ou mais.
3. Use stale-while-revalidate
No Next.js 15, profile="max" aplica essa estratégia:
revalidateTag('posts', { profile: 'max' })
O usuário sempre recebe conteúdo armazenado rapidamente, enquanto o sistema atualiza o cache em segundo plano. Isso proporciona uma experiência melhor.
4. Monitore a taxa de acerto do cache
Adicione isto a .env.local:
NEXT_PRIVATE_DEBUG_CACHE=1
Depois, inicie o servidor de produção. O console exibirá os acertos do cache:
○ GET /blog 200 in 45ms (cache: HIT)
○ GET /about 200 in 12ms (cache: SKIP)
Confira periodicamente se sua estratégia está funcionando.
Diferenças entre desenvolvimento e produção
Aviso importante:
O cache se comporta de forma completamente diferente em desenvolvimento, com npm run dev, e em produção, com npm start.
| Recurso | Desenvolvimento | Produção |
|---|---|---|
| Data Cache | Quase todo desativado | Totalmente ativado |
| Full Route Cache | Desativado | Ativado para rotas estáticas |
| Request Memoization | Ativado | Ativado |
| Router Cache | Ativado, mas por pouco tempo | Intervalo completo |
Forma correta de testar o cache:
# 1. Gere o build de produção
npm run build
# 2. Confira o tipo de cada rota na saída
# ○ = estática, usa cache
# ƒ = dinâmica, não usa cache
# 3. Inicie o servidor de produção
npm start
# 4. Teste o comportamento do cache
# Abra a página no navegador e depois altere a fonte de dados
# Atualize a página e confira se os dados antigos ainda aparecem
# 5. Teste revalidate
# Espere o intervalo terminar, acesse de novo e confira a atualização
Não depure problemas de cache no ambiente de desenvolvimento. Esse é o erro mais comum: muita gente perde horas em desenvolvimento e conclui que revalidate não funciona.
Conclusão
Depois de tudo isso, os pontos essenciais são estes:
1. Entenda a função das quatro camadas
Não as confunda. O Router Cache fica no cliente; os outros três ficam no servidor. revalidate afeta principalmente o Data Cache e o Full Route Cache.
2. Escolha o método de revalidação adequado
- Atualização periódica →
export const revalidate = 3600. - Ação do usuário que afeta uma página →
revalidatePath('/page'). - Ação do usuário que afeta várias páginas →
revalidateTag('tag'). - Invalidação imediata →
updateTag('tag')no Next.js 15.
3. Teste em produção
Lembre-se sempre: o comportamento do cache em desenvolvimento não é representativo. Para testá-lo, use npm run build && npm start.
4. Investigue uma camada por vez
Quando os dados não forem atualizados, confira nesta ordem:
- Router Cache no cliente → faça uma atualização forçada.
- Full Route Cache no servidor → use revalidatePath.
- Data Cache no servidor → use revalidateTag.
- A própria fonte de dados.
5. O explícito é melhor que o implícito, a filosofia do Next.js 15
O Next.js 15 mudou o cache de ativado por padrão para desativado por padrão. Isso obriga você a decidir conscientemente quais dados devem ser armazenados. Dá mais trabalho, mas torna o código mais claro e fácil de manter no longo prazo.
Se você chegou até aqui, já tem uma visão completa do cache no Next.js. Na próxima vez que os dados não forem atualizados, saberá por onde começar.
O cache é complexo, mas, depois de entendido, torna-se um dos recursos mais poderosos do Next.js. Usado com cuidado, ele deixa seu aplicativo muito rápido; usado sem critério, vira uma armadilha criada por você mesmo.
Que seu aplicativo tenha desempenho excelente e zero bugs!
Fluxo completo para usar o cache do Next.js
Etapas completas para entender as quatro camadas de cache, escolher o método de revalidação e investigar dados que não são atualizados.
⏱️ Estimated time: 2 hr
- 1
Step 1: Entenda as quatro camadas de cache do Next.js
As quatro camadas são:
1. Request Memoization (eliminação de solicitações duplicadas)
• Dentro da mesma renderização, uma URL idêntica gera apenas uma solicitação
• É automática e não exige configuração
• Ciclo de vida: uma única solicitação
2. Data Cache (cache do fetch)
• Armazena em cache as solicitações feitas com fetch
• Comportamento padrão: o Next.js 14 armazena; o 15 não
• Configuração: opção cache
3. Full Route Cache (cache completo da rota)
• Armazena o HTML de toda a rota
• Páginas estáticas são armazenadas automaticamente
• Configuração: revalidate
4. Router Cache (cache de rotas no cliente)
• Cache usado durante a navegação no cliente
• É automático e não exige configuração
• Ciclo de vida: durante a sessão
Ponto-chave: o Router Cache fica no cliente; os outros três ficam no servidor. - 2
Step 2: Escolha o método de revalidação adequado
Há três opções:
1. Atualização periódica (export const revalidate)
```tsx
export const revalidate = 3600 // Revalida após 3.600 segundos
```
• Indicado para: conteúdo atualizado periodicamente
• Configuração: em page.tsx ou layout.tsx
2. Ação do usuário que afeta uma página (revalidatePath)
```tsx
import { revalidatePath } from 'next/cache'
revalidatePath('/blog/post-1')
```
• Indicado para: atualizar uma única página após uma ação do usuário
• Uso: em uma Server Action ou API Route
3. Ação do usuário que afeta várias páginas (revalidateTag)
```tsx
import { revalidateTag } from 'next/cache'
// Adicione a tag ao fazer o fetch
fetch(url, { next: { tags: ['posts'] } })
// Invalide a tag após a atualização
revalidateTag('posts')
```
• Indicado para: atualizar várias páginas após uma ação do usuário
• Uso: em uma Server Action ou API Route
Como escolher:
• Atualização periódica → export const revalidate
• Uma página → revalidatePath
• Várias páginas → revalidateTag - 3
Step 3: Investigue dados que não são atualizados
Ordem de diagnóstico:
1. Verifique se você está no ambiente de desenvolvimento
• O comportamento do cache nesse ambiente não é representativo
• Teste em produção: npm run build && npm start
2. Verifique o Router Cache (cliente)
• Faça uma atualização forçada (Ctrl+Shift+R)
• Limpe o cache do navegador
3. Verifique o Full Route Cache (servidor)
• Invalide-o com revalidatePath
• Confira a configuração de revalidate
4. Verifique o Data Cache (servidor)
• Invalide-o com revalidateTag
• Confira a configuração cache do fetch
5. Verifique a fonte de dados
• Confirme se ela realmente foi atualizada
• Confira os dados retornados pela API
Problemas comuns:
• Testar no ambiente de desenvolvimento → use o ambiente de produção
• Router Cache não invalidado → faça uma atualização forçada
• Configuração incorreta de revalidate → confira a configuração
• Next.js 15 não armazena por padrão → configure cache explicitamente - 4
Step 4: Entenda as diferenças de cache entre Next.js 14 e 15
Next.js 14:
• fetch usa cache por padrão, de forma equivalente a getStaticProps
• Para desativá-lo, é preciso definir cache: 'no-store'
Next.js 15:
• fetch não usa cache por padrão
• Para ativá-lo, é preciso definir cache: 'force-cache'
Recomendações para a migração:
• Revise todas as chamadas fetch
• Configure a opção cache explicitamente
• Teste o comportamento do cache
Exemplo:
```tsx
// Next.js 14 (cache por padrão)
fetch(url) // Armazenado automaticamente
// Next.js 15 (sem cache por padrão)
fetch(url, { cache: 'force-cache' }) // Exige configuração explícita
```
Ponto-chave: a filosofia do Next.js 15 é que o explícito é melhor que o implícito; você precisa decidir conscientemente quais dados devem ser armazenados em cache.
FAQ
Quantas camadas de cache o Next.js tem e o que cada uma controla?
1. Request Memoization
• Dentro da mesma renderização, uma URL idêntica gera apenas uma solicitação
• É automática e não exige configuração
• Ciclo de vida: uma única solicitação
2. Data Cache
• Cache das solicitações fetch
• Por padrão, o Next.js 14 armazena; o 15 não
• Configuração: opção cache
3. Full Route Cache
• Cache do HTML de toda a rota
• Páginas estáticas são armazenadas automaticamente
• Configuração: revalidate
4. Router Cache
• Cache usado durante a navegação no cliente
• É automático e não exige configuração
• Ciclo de vida: durante a sessão
Ponto-chave: o Router Cache fica no cliente; os outros três ficam no servidor. revalidate afeta principalmente o Data Cache e o Full Route Cache.
Qual é a diferença entre revalidatePath, revalidateTag e updateTag?
• Invalida o cache de um caminho específico
• Indicado para atualizar uma página após uma ação do usuário
• Uso: revalidatePath('/blog/post-1')
revalidateTag (várias páginas):
• Invalida todo o cache associado a uma tag
• Indicado para atualizar várias páginas após uma ação do usuário
• Uso: adicione a tag ao fetch e invalide-a após a atualização
updateTag (invalidação imediata, no Next.js 15):
• Marca a tag imediatamente como expirada
• Indicado quando a invalidação precisa ser imediata
• Uso: updateTag('tag')
Como escolher:
• Uma página → revalidatePath
• Várias páginas → revalidateTag
• Invalidação imediata → updateTag (Next.js 15)
Observação: revalidateTag e updateTag devem ser usados com as tags do fetch.
Por que os dados continuam desatualizados mesmo depois de configurar revalidate?
1. Teste no ambiente de desenvolvimento
• O comportamento do cache não é representativo
• Use o ambiente de produção: npm run build && npm start
2. Router Cache não invalidado
• O cache do cliente ainda está ativo
• Faça uma atualização forçada (Ctrl+Shift+R)
3. Configuração incorreta de revalidate
• Confira o valor de revalidate
• Confira onde a configuração foi declarada
4. Next.js 15 não usa cache por padrão
• É preciso definir cache: 'force-cache'
• Confira a opção cache do fetch
5. A fonte de dados não foi atualizada
• Confirme se a alteração realmente ocorreu
• Confira os dados retornados pela API
Ordem de diagnóstico:
1. Ambiente de desenvolvimento
2. Router Cache (atualização forçada)
3. Full Route Cache (revalidatePath)
4. Data Cache (revalidateTag)
5. Fonte de dados
Qual é a diferença entre o cache do Next.js 14 e o do 15?
Next.js 14:
• fetch usa cache por padrão, de forma equivalente a getStaticProps
• Para desativá-lo, use cache: 'no-store'
• Comportamento: cache implícito
Next.js 15:
• fetch não usa cache por padrão
• Para ativá-lo, use cache: 'force-cache'
• Comportamento: configuração explícita
Recomendações para a migração:
• Revise todas as chamadas fetch
• Configure a opção cache explicitamente
• Teste o comportamento do cache
Exemplo:
```tsx
// Next.js 14 (cache por padrão)
fetch(url) // Armazenado automaticamente
// Next.js 15 (sem cache por padrão)
fetch(url, { cache: 'force-cache' }) // Exige configuração explícita
```
Ponto-chave: a filosofia do Next.js 15 é que o explícito é melhor que o implícito; você precisa decidir conscientemente quais dados devem ser armazenados em cache. É uma mudança incompatível que exige atenção durante a migração.
Quando devo usar revalidatePath e quando devo usar revalidateTag?
• Indicado para atualizar uma única página após uma ação do usuário
• Exemplo: atualizar a página de detalhes depois de editar um post
• Uso: revalidatePath('/blog/post-1')
revalidateTag (várias páginas):
• Indicado para atualizar várias páginas após uma ação do usuário
• Exemplo: atualizar todas as listagens após publicar um post
• Uso: adicione a tag ao fetch e invalide-a após a atualização
Como escolher:
• Atualizar uma página → revalidatePath
• Atualizar várias páginas → revalidateTag
Exemplo:
```tsx
// Uma página
revalidatePath('/blog/post-1')
// Várias páginas
fetch(url, { next: { tags: ['posts'] } })
revalidateTag('posts') // Invalida todo cache com a tag 'posts'
```
Ponto-chave: revalidateTag deve ser usado com as tags do fetch e oferece mais flexibilidade.
O cache se comporta da mesma forma nos ambientes de desenvolvimento e produção?
Desenvolvimento:
• O comportamento do cache é instável
• Pode não haver armazenamento em cache
• Não é adequado para testar cache
Produção:
• O comportamento é o esperado
• O cache funciona normalmente
• É o ambiente adequado para testes
Para testar o cache:
• Use o ambiente de produção: npm run build && npm start
• Não teste o cache no ambiente de desenvolvimento
Erros comuns:
• Testar cache em desenvolvimento → resultado impreciso
• Concluir que revalidate não funciona → o problema é o ambiente
Recomendação: sempre teste o comportamento do cache em produção.
24 min de leitura · Publicado em: 19 dez 2025 · Atualizado em: 4 set 2026
Guia completo Next.js
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.
Anterior
Guia de gerenciamento de estado no Next.js: Zustand vs Jotai na prática
Redux é pesado demais e Context tem problemas de desempenho? Este artigo compara Zustand e Jotai no Next.js e apresenta um guia claro de escolha e boas práticas para o App Router.
Parte 24 de 51
Próximo
Otimização de imagens no Next.js: guia completo do componente Image
Aprenda a usar o componente Image do Next.js para corrigir carregamento lento, erros de configuração de imagens remotas e mudanças de layout. O guia inclui recursos das versões 14 e 15, exemplos práticos e técnicas de otimização de desempenho capazes de reduzir o tamanho das imagens em 60% a 80%.
Parte 26 de 51



Comentários
Entre com GitHub para comentar