Performance é crucial para qualquer aplicação web moderna. Portanto, entender estratégias de cache no Next.js pode transformar completamente a experiência do usuário. Além disso, implementar cache corretamente reduz custos de infraestrutura e melhora o SEO. Neste guia, você vai aprender as principais estratégias de cache Next.js e como aplicá-las na prática.

O Que É Cache no Next.js?

Cache é como uma “memória rápida” da sua aplicação. Em outras palavras, é uma cópia temporária de dados armazenada para acesso instantâneo. Assim, quando um usuário visita sua página, ela carrega muito mais rápido.

Imagine um restaurante que prepara alguns pratos populares antecipadamente. Portanto, quando um cliente pede, o prato sai na hora. Consequentemente, o atendimento fica mais rápido. Da mesma forma, o Next.js “prepara” páginas antecipadamente para entregar rapidamente aos usuários.

O Next.js oferece várias camadas de cache: geração estática, revalidação incremental e cache de dados. Além disso, cada estratégia serve para cenários diferentes. Por exemplo, blogs usam regeneração, enquanto landing pages usam geração estática pura.

Static Site Generation (SSG)

SSG gera páginas HTML no momento do build. Assim, o servidor entrega arquivos prontos instantaneamente. Essa é a estratégia mais rápida possível.

Use SSG quando o conteúdo muda pouco ou nada. Por exemplo, páginas institucionais, documentação e landing pages. Dessa forma, você obtém performance máxima com custo mínimo.

Implementação básica:

// app/sobre/page.tsx
export default async function SobrePage() {
  return (
    <div>
      <h1>Sobre Nós</h1>
      <p>Conteúdo estático que raramente muda</p>
    </div>
  )
}

// Por padrão, componentes Server são SSG
// Página gerada uma vez no build

Com SSG, você pré-renderiza rotas dinâmicas usando generateStaticParams. Portanto, mesmo páginas dinâmicas ficam estáticas:

// app/produto/[id]/page.tsx
export async function generateStaticParams() {
  const produtos = await fetch('https://api.exemplo.com/produtos')
  const data = await produtos.json()

  return data.map((produto) => ({
    id: produto.id.toString()
  }))
}

export default async function ProdutoPage({ params }) {
  const produto = await fetch(`https://api.exemplo.com/produtos/${params.id}`)
  const data = await produto.json()

  return <div>{data.nome}</div>
}

// Gera /produto/1, /produto/2, etc. no build

Incremental Static Regeneration (ISR)

ISR combina o melhor dos dois mundos: velocidade do estático com atualização do dinâmico. Em outras palavras, páginas são geradas estaticamente, mas regeneram automaticamente após um período.

Pense em um jornal digital que imprime edições a cada hora. Assim, leitores sempre veem conteúdo recente, mas não esperam pela geração. Consequentemente, você tem performance com dados atualizados.

Use ISR para blogs, e-commerce e dashboards com dados que mudam periodicamente. Por exemplo, artigos de blog podem regenerar a cada hora. Dessa forma, novos posts aparecem sem rebuild completo.

Implementação com revalidação temporal:

// app/blog/page.tsx
export const revalidate = 3600 // 1 hora em segundos

export default async function BlogPage() {
  const posts = await fetch('https://api.exemplo.com/posts')
  const data = await posts.json()

  return (
    <div>
      {data.map(post => (
        <article key={post.id}>
          <h2>{post.titulo}</h2>
        </article>
      ))}
    </div>
  )
}

// Página regenera automaticamente após 1 hora
// Primeira requisição após 1h serve cache antigo
// Segunda requisição já vê a versão atualizada

O ISR funciona em dois tempos. Primeiro, serve a página em cache. Depois, regenera em background. Portanto, usuários nunca esperam pela regeneração. Além disso, você controla a frequência de atualização.

On-Demand Revalidation

Revalidação sob demanda invalida o cache quando você quiser. Por exemplo, quando publica um novo post ou atualiza um produto. Assim, o cache atualiza imediatamente, não após um tempo.

Imagine uma loja que repõe prateleiras assim que recebe mercadoria nova. Consequentemente, clientes sempre veem os produtos mais recentes. Da mesma forma, revalidação sob demanda atualiza o cache instantaneamente.

Criando uma API Route para revalidação:

// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache'
import { NextRequest, NextResponse } from 'next/server'

export async function POST(request: NextRequest) {
  const body = await request.json()
  const { secret, path, tag } = body

  // Validar token de segurança
  if (secret !== process.env.REVALIDATE_SECRET) {
    return NextResponse.json({ error: 'Token inválido' }, { status: 401 })
  }

  try {
    // Revalidar caminho específico
    if (path) {
      revalidatePath(path)
      return NextResponse.json({ revalidated: true, path })
    }

    // Ou revalidar por tag
    if (tag) {
      revalidateTag(tag)
      return NextResponse.json({ revalidated: true, tag })
    }

    return NextResponse.json({ error: 'Path ou tag necessário' }, { status: 400 })
  } catch (error) {
    return NextResponse.json({ error: 'Erro ao revalidar' }, { status: 500 })
  }
}

Chamando a API de revalidação (exemplo com webhook do WordPress):

// Configuração de webhook no WordPress
// URL: https://seusite.com/api/revalidate
// Method: POST
// Body:
{
  "secret": "seu-token-secreto",
  "path": "/blog"
}

// Quando publicar um post, WordPress chama a API
// Cache da página /blog invalida automaticamente

Use revalidação sob demanda com sistemas CMS, e-commerce e painéis admin. Portanto, quando atualizar dados no backend, o cache atualiza instantaneamente no frontend. Além disso, você combina performance de cache com dados sempre atualizados.

Cache de Dados com Fetch

Next.js estende o fetch nativo com cache automático. Assim, você controla como cada requisição é cacheada. Consequentemente, otimiza cada chamada de API individualmente.

Opções de cache no fetch:

// Cache padrão - armazena indefinidamente
const dados1 = await fetch('https://api.exemplo.com/dados', {
  cache: 'force-cache' // Padrão no Next.js 14+
})

// Sem cache - sempre busca dados frescos
const dados2 = await fetch('https://api.exemplo.com/dados', {
  cache: 'no-store' // Útil para dados em tempo real
})

// Cache com revalidação temporal
const dados3 = await fetch('https://api.exemplo.com/dados', {
  next: { revalidate: 60 } // Revalida a cada 60 segundos
})

// Cache com tags para revalidação seletiva
const dados4 = await fetch('https://api.exemplo.com/posts', {
  next: { tags: ['posts'] }
})

// Depois, invalida apenas esse cache:
// revalidateTag('posts')

Exemplo prático combinando estratégias:

// app/dashboard/page.tsx
export const revalidate = 300 // 5 minutos

export default async function DashboardPage() {
  // Dados de usuário - sempre frescos
  const usuario = await fetch('https://api.exemplo.com/usuario', {
    cache: 'no-store'
  })

  // Estatísticas - cache de 5 minutos
  const stats = await fetch('https://api.exemplo.com/stats', {
    next: { revalidate: 300 }
  })

  // Configurações - cache permanente
  const config = await fetch('https://api.exemplo.com/config', {
    cache: 'force-cache'
  })

  return (
    <div>
      <h1>Olá, {usuario.nome}</h1>
      <p>Vendas hoje: {stats.vendas}</p>
    </div>
  )
}

Escolhendo a Estratégia Certa

Cada projeto tem necessidades diferentes. Portanto, escolha a estratégia baseado em frequência de atualização e criticidade dos dados.

Use SSG quando:

  • Conteúdo raramente muda (institucional, documentação)
  • Performance é prioridade máxima
  • Você quer custos mínimos de servidor

Use ISR quando:

  • Conteúdo atualiza periodicamente (blogs, notícias)
  • Você quer balance entre performance e atualização
  • Tem muitas páginas dinâmicas

Use On-Demand Revalidation quando:

  • Usa CMS ou sistema de gestão de conteúdo
  • Precisa atualizar cache imediatamente
  • Quer controle total sobre invalidação

Use no-store quando:

  • Dados mudam constantemente (tempo real)
  • Informações personalizadas por usuário
  • Dados sensíveis que não devem ser cacheados

Na prática, você provavelmente vai combinar estratégias. Por exemplo, ISR na listagem de posts com revalidação sob demanda. Dessa forma, otimiza performance enquanto mantém conteúdo atualizado.

Conclusão

Dominar estratégias de cache no Next.js é essencial para criar aplicações rápidas e eficientes. Portanto, comece identificando quais páginas mudam com frequência. Depois, aplique a estratégia adequada para cada caso.

Lembre-se: cache bem implementado melhora performance, reduz custos e aumenta satisfação do usuário. Além disso, o Next.js facilita com APIs simples e intuitivas. Consequentemente, você implementa cache avançado com poucas linhas de código.

Comece testando ISR em uma página de blog. Assim, você experimenta os benefícios sem complexidade. Em seguida, adicione revalidação sob demanda para controle total. Por fim, otimize cada requisição com as opções de fetch.

Quer aprender mais sobre Next.js? Confira nosso guia sobre Server Components no Next.js e descubra como otimizar ainda mais sua aplicação.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *