Cache Components no Next.js 16 em 2026: use cache, PPR e Perguntas de Entrevista

Análise aprofundada dos Cache Components do Next.js 16: a diretiva use cache, Partial Pre-Rendering (PPR), cacheLife, cacheTag, Instant Navigations do 16.3 e perguntas de entrevista técnica para desenvolvedores seniores.

Cache Components no Next.js 16: use cache, PPR e perguntas de entrevista

Os Cache Components do Next.js 16 representam a maior mudança na forma como o Next.js lida com cache desde a introdução do App Router. O modelo antigo cacheava tudo por padrão e exigia opt-out. O novo modelo não cacheia nada por padrão e exige opt-in com a diretiva "use cache". Com Next.js 16.3, lançado em agosto de 2026, os Cache Components ganharam Instant Navigations: um conjunto de ferramentas que traz responsividade de SPA para aplicações dirigidas pelo servidor.

A Mudança Fundamental no Modelo Mental

Next.js 16 passa de um cache implícito (tudo é cacheado, opt-out com APIs dinâmicas) para um cache explícito (nada é cacheado, opt-in com "use cache"). Next.js 16.3 constrói sobre isso com Partial Prefetching e Instant Navigations, fazendo o modelo explícito parecer tão rápido quanto uma single-page app.

Por que o Next.js 16 substituiu o cache implícito

O modelo de cache implícito do Next.js 14-15 causava problemas de previsibilidade. Uma chamada fetch dentro de um Server Component era automaticamente deduplicada e cacheada, mas determinar se uma página era estática ou dinâmica dependia de quais APIs ela utilizava. Depurar o comportamento do cache exigia compreender múltiplas camadas ocultas: o fetch cache, o full-route cache e o router cache.

O Next.js 16 remove os três caches implícitos. Cada página é renderizada dinamicamente a cada requisição, a menos que seja explicitamente marcada com "use cache". O export revalidate desaparece. O unstable_cache é substituído pela diretiva "use cache" gerenciada pelo compilador. O blog de lançamento do Next.js 16 detalha o escopo completo dessas mudanças.

Essa mudança troca otimização automática por controle explícito. O desempenho pode cair inicialmente para aplicações que dependiam do cache implícito, mas a experiência de depuração melhora drasticamente: o conteúdo cacheado está cacheado porque o código diz isso, não por causa de heurísticas do framework.

Como a diretiva use cache funciona em três escopos

A diretiva "use cache" opera em três níveis: arquivo, componente e função. Escolher o escopo correto é a decisão de cache mais importante no Next.js 16.

O cache em nível de arquivo marca cada export assíncrono em um arquivo como cacheável. É adequado para páginas com conteúdo completamente estático sem dados de usuário.

app/pricing/page.tsxtypescript
"use cache"

import { getPricingPlans } from "@/lib/data"

// Entire page is cached as a static shell
export default async function PricingPage() {
  const plans = await getPricingPlans()
  return (
    <section>
      {plans.map((plan) => (
        <PricingCard key={plan.id} plan={plan} />
      ))}
    </section>
  )
}

O cache em nível de componente cacheia componentes individuais dentro de uma página. Isso habilita o Partial Pre-Rendering: o componente cacheado é renderizado como parte do shell estático, enquanto componentes dinâmicos chegam via streaming.

components/ProductRecommendations.tsxtsx
async function ProductRecommendations({ categoryId }: { categoryId: string }) {
  "use cache"
  // categoryId becomes part of the automatic cache key
  const products = await getTopProducts(categoryId)
  return (
    <ul>
      {products.map((p) => (
        <li key={p.id}>{p.name} - {p.price}</li>
      ))}
    </ul>
  )
}

O cache em nível de função é aplicado diretamente a funções de busca de dados. Isso substitui o antigo padrão unstable_cache.

lib/data.tstypescript
import { cacheLife } from "next/cache"

export async function getArticleBySlug(slug: string) {
  "use cache"
  cacheLife("hours")
  // slug is automatically included in the cache key
  const article = await db.article.findUnique({ where: { slug } })
  return article
}

O compilador gera automaticamente as chaves de cache a partir dos argumentos da função. Não é necessário usar arrays keyParts manuais nem workarounds com JSON.stringify. Os argumentos devem ser serializáveis (strings, números, objetos simples). Passar uma instância de classe ou uma função como argumento quebra a serialização.

Instant Navigations no Next.js 16.3

O Next.js 16.3 responde à crítica mais comum dos Server Components: as navegações parecem lentas porque exigem um round-trip de rede. Instant Navigations corrige isso fazendo prefetch de shells reutilizáveis por rota, não por link.

Habilitar Instant Navigations com dois flags em next.config.ts:

next.config.tstypescript
import type { NextConfig } from "next"

const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
}

export default nextConfig

Com partialPrefetching: true, o Next.js extrai um loading shell de cada rota e o cacheia no cliente. Quando um usuário clica em um link, o shell renderiza instantaneamente enquanto o conteúdo dinâmico chega via streaming. É o mesmo padrão de responsividade que single-page apps usam, mas sem abrir mão da renderização do lado do servidor.

Stream, Cache ou Block

Para cada operação assíncrona em uma rota, a escolha é: Stream com <Suspense> (estado de carregamento instantâneo), Cache com "use cache" (UI cacheada instantânea), ou Block com export const instant = false (esperar pelo servidor). As duas primeiras produzem navegações instantâneas.

O Navigation Inspector nas DevTools do Next.js permite pausar navegações no shell para ver exatamente o que é prefetchado. Instant Insights detecta automaticamente navegações lentas durante o desenvolvimento, transformando-as em erros acionáveis.

Partial Pre-Rendering com Partial Prefetching

Partial Pre-Rendering (PPR) era experimental no Next.js 14-15. No Next.js 16, PPR é estável e está integrado diretamente nos Cache Components através de cacheComponents: true. O Next.js 16.3 estende isso com Partial Prefetching, que muda como os shells são entregues ao cliente.

Antes do 16.3, o Next.js enviava uma requisição de prefetch para cada link no viewport. Com Partial Prefetching, ele faz prefetch de um shell por rota. Vinte links de chat apontando para /chat/[id] disparam um prefetch, não vinte. Isso reduz a carga de rede e torna a estratégia de prefetch similar a como SPAs fazem code-split por rota.

app/dashboard/page.tsxtsx
import { Suspense } from "react"
import { UserGreeting } from "@/components/UserGreeting"
import { StaticSidebar } from "@/components/StaticSidebar"
import { RecentActivity } from "@/components/RecentActivity"

export default function DashboardPage() {
  return (
    <div className="grid grid-cols-12 gap-6">
      {/* Cached static shell - prefetched and served instantly */}
      <StaticSidebar />

      <main className="col-span-9">
        {/* Dynamic - streams in after shell renders */}
        <Suspense fallback={<GreetingSkeleton />}>
          <UserGreeting />
        </Suspense>

        {/* Dynamic - streams independently */}
        <Suspense fallback={<ActivitySkeleton />}>
          <RecentActivity />
        </Suspense>
      </main>
    </div>
  )
}

A árvore de decisão de renderização: componentes com "use cache" fazem parte do shell estático. Componentes envolvidos em <Suspense> que leem cookies, headers ou outros dados específicos da requisição são transmitidos dinamicamente. Para prefetching por link além do shell, adicionar <Link prefetch={true}> a links específicos.

Perfis cacheLife: substituindo revalidate

O export revalidate do Next.js 15 desaparece. Em seu lugar, cacheLife() fornece perfis nomeados que controlam a duração do cache. Os perfis integrados incluem seconds, minutes, hours, days, weeks e max.

lib/data.tstypescript
import { cacheLife } from "next/cache"

export async function getExchangeRates() {
  "use cache"
  cacheLife("minutes") // Revalidates every few minutes
  const rates = await fetch("https://api.exchangerate.host/latest")
  return rates.json()
}

export async function getCompanyInfo() {
  "use cache"
  cacheLife("weeks") // Rarely changes
  return db.company.findFirst()
}

Perfis personalizados são definidos no next.config.ts:

next.config.tstypescript
import type { NextConfig } from "next"

const config: NextConfig = {
  cacheComponents: true,
  cacheLife: {
    // Custom profile for product data
    product: {
      stale: 300,      // Serve stale for 5 minutes
      revalidate: 3600, // Revalidate in background every hour
      expire: 86400,    // Hard expire after 24 hours
    },
  },
}

export default config

Centralizar os perfis na configuração significa que uma única alteração ajusta o cache em toda a aplicação. Isso elimina os valores revalidate: 3600 espalhados que eram problemáticos nas bases de código do Next.js 15.

Uma regra para lembrar: cacheLife() deve ser executado apenas uma vez por invocação de função. O cache condicional é válido apenas se uma única ramificação é executada.

Pronto para mandar bem nas entrevistas de React / Next.js?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

cacheTag e updateTag para invalidação direcionada

Sem cacheTag(), uma função cacheada só pode expirar por tempo. A invalidação sob demanda requer etiquetar as entradas de cache e chamar revalidateTag() ou o novo updateTag() em uma Server Action.

lib/data.tstypescript
import { cacheLife, cacheTag } from "next/cache"

export async function getProductById(id: string) {
  "use cache"
  cacheTag(`product-${id}`, "products")
  cacheLife("days")
  return db.product.findUnique({ where: { id } })
}
app/actions.tstypescript
"use server"

import { updateTag } from "next/cache"

export async function updateProduct(id: string, data: ProductUpdate) {
  await db.product.update({ where: { id }, data })
  // Invalidate this specific product AND the product list
  updateTag(`product-${id}`)
  updateTag("products")
}

A diferença entre revalidateTag e updateTag: ambos invalidam, mas updateTag é a primitiva recomendada no Next.js 16.3, projetada para funcionar de forma fluida com o novo modelo de cache. As tags suportam até 256 caracteres cada, com um máximo de 128 tags por entrada de cache.

Armadilha de tags ausentes

Uma função cacheada sem cacheTag() só pode expirar por tempo. A invalidação sob demanda é impossível. Isso é fácil de esquecer durante o desenvolvimento inicial e doloroso de descobrir quando um cliente reporta dados desatualizados em produção.

Segurança: variantes de use cache

A diretiva "use cache" por padrão cria um cache compartilhado. Qualquer combinação de argumentos produz uma entrada de cache que pode ser servida para qualquer usuário. Isso é correto para dados públicos mas perigoso para conteúdo personalizado.

"use cache: private" cria um cache por usuário que inclui a sessão atual na chave de cache. Pode acessar com segurança cookies() e headers() dentro do escopo cacheado.

"use cache: remote" persiste o cache em armazenamento externo. Em ambientes serverless (Vercel, AWS Lambda), o cache em memória padrão é perdido durante cold starts. O cache remoto garante que as entradas de cache sobrevivam entre instâncias de função, embora exija um round-trip de rede e tipicamente incorra em custos de plataforma.

typescript
// WRONG: User data in shared cache - data leak risk
export async function getUserDashboard(userId: string) {
  "use cache"
  return db.user.findUnique({
    where: { id: userId },
    include: { orders: true, preferences: true },
  })
}

// CORRECT: Private cache scoped to the current user
export async function getUserDashboard() {
  "use cache: private"
  cacheLife("minutes")
  const session = await cookies()
  const userId = session.get("userId")?.value
  return db.user.findUnique({
    where: { id: userId },
    include: { orders: true, preferences: true },
  })
}

Uma matriz de decisão para entrevistas:

DirectiveScopeUse When
"use cache"Shared, all usersPublic data: pricing, articles, product catalogs
"use cache: private"Per-user sessionPersonalized data: dashboards, settings, order history
"use cache: remote"Shared, external storageHigh-traffic data in serverless environments

Root Params no Next.js 16.3

O Next.js 16.3 introduz root params, que resolvem o excesso de prop-drilling para segmentos dinâmicos definidos acima do layout raiz. Root params como [lang] são efetivamente globais e necessários em toda a árvore de componentes.

app/[lang]/posts/[slug]/page.tsxtsx
import { lang } from "next/root-params"

export default async function PostPage(
  props: PageProps<"/[lang]/posts/[slug]">
) {
  const { slug } = await props.params
  const language = await lang()

  return (
    <article>
      <p>Language: {language}</p>
      <p>Post: {slug}</p>
    </article>
  )
}

Root params funcionam dentro de escopos use cache, e apenas os params que são realmente lidos se tornam parte da chave de cache. Isso torna os padrões de internacionalização mais ergonômicos sem quebrar o comportamento do cache.

Testando Instant Navigations

O helper de teste instant() para Playwright permite assertar qual conteúdo é visível imediatamente após uma navegação, sem esperar pela rede. Isso detecta regressões onde um refactoring acidentalmente torna uma navegação lenta.

e2e/instant-navigation.spec.tstypescript
import { expect, test } from "@playwright/test"
import { instant } from "@next/playwright"

test("product title is available immediately", async ({ page }) => {
  await page.goto("/products/shoes")

  // Assert what's visible without waiting for network
  await instant(page, async () => {
    await page.click('a[href="/products/hats"]')
    await expect(page.locator("h1")).toContainText("Baseball Cap")
    await expect(page.getByText("Checking inventory...")).toBeVisible()
  })

  await expect(page.getByText("12 in stock")).toBeVisible()
})

Esse padrão garante que páginas projetadas para carregar instantaneamente permaneçam instantâneas através das mudanças de código. O Navigation Inspector nas DevTools complementa isso permitindo inspecionar visualmente os shells durante o desenvolvimento.

Perguntas de entrevista: o que desenvolvedores seniores são perguntados

Estas perguntas refletem os padrões de entrevista reais em 2026 para posições Next.js seniores. Cada uma visa um aspecto específico dos Cache Components e das atualizações do 16.3.

P1: Explicar a mudança do cache implícito para o explícito no Next.js 16. Por que o framework fez essa mudança?

O Next.js 14-15 cacheava chamadas fetch e páginas implicitamente. Depurar se uma página era estática ou dinâmica exigia rastrear múltiplas camadas de cache ocultas. O modelo explícito com "use cache" torna o cache visível no código-fonte. O trade-off: o desempenho pode cair inicialmente para aplicações migrando do cache implícito, mas os desenvolvedores ganham controle total e previsibilidade.

P2: Quais são os três escopos de "use cache" e quando cada um deve ser usado?

Nível de arquivo para páginas completamente estáticas. Nível de componente para misturar conteúdo cacheado e dinâmico dentro de uma página (o padrão PPR). Nível de função para cachear operações específicas de busca de dados. A escolha do escopo determina a granularidade do cache e os limites de invalidação.

P3: Como o Partial Prefetching do 16.3 difere do prefetching do 16.0?

No 16.0, o Next.js enviava uma requisição de prefetch para cada link no viewport. No 16.3 com partialPrefetching: true, ele faz prefetch de um shell reutilizável por rota, cacheado no cliente durante toda a sessão. Vinte links para /chat/[id] disparam um prefetch para o shell da rota chat, não vinte requisições separadas. Isso reduz a carga de rede e se alinha com como SPAs fazem code-split.

P4: Uma equipe cacheia uma função que retorna o histórico de pedidos de um usuário com "use cache". O que acontece?

O cache compartilhado armazena o resultado indexado pelos argumentos da função. Se a função aceita um parâmetro userId, diferentes usuários obtêm diferentes entradas de cache, mas o cache continua sendo infraestrutura compartilhada. Se a função lê userId de cookies() em vez de parâmetros, o build falha porque cookies() é uma API de runtime proibida no escopo do cache compartilhado. A solução: mudar para "use cache: private" ou passar o user ID como argumento explícito.

P5: Como cacheLife difere do antigo export revalidate?

revalidate era um número único (segundos) definido no nível de página ou layout. cacheLife usa perfis nomeados com três dimensões: stale (servir conteúdo desatualizado), revalidate (intervalo de atualização em background) e expire (expiração forçada). Os perfis são centralizados no next.config.ts, então uma única alteração afeta todos os pontos de chamada que usam esse perfil.

P6: Qual é a diferença entre revalidateTag e updateTag?

Ambos invalidam entradas de cache por tag. updateTag é a primitiva recomendada no Next.js 16.3, projetada para se integrar de forma limpa com o modelo de cache explícito. Na prática, ambos funcionam para invalidação sob demanda, mas updateTag é a API orientada para o futuro.

P7: Quando usar export const instant = false?

Quando uma rota deve intencionalmente bloquear a navegação até que o servidor responda. Por exemplo, um blog pode nunca mostrar um loading shell para os posts, preferindo que os leitores vejam o conteúdo completo imediatamente. Isso opta-out dos erros de Instant Insights para essa rota.

Para mais perguntas de entrevista sobre data fetching no Next.js, o SharpSkill oferece módulos de prática com sessões cronometradas e explicações detalhadas. O módulo de Server Actions no Next.js cobre os padrões de Server Actions que se associam à invalidação de cache.

Checklist prático para Cache Components em produção

  • Habilitar cacheComponents: true e partialPrefetching: true no next.config.ts
  • Auditar cada página: adicionar "use cache" a páginas estáticas e funções de busca de dados que servem conteúdo público
  • Envolver todo o conteúdo dinâmico (específico do usuário, em tempo de requisição) em boundaries <Suspense> com fallbacks skeleton significativos
  • Usar "use cache: private" para qualquer função que acesse cookies, headers ou retorne dados personalizados
  • Considerar "use cache: remote" para dados de alto tráfego em ambientes serverless
  • Definir perfis cacheLife personalizados para categorias de dados comuns (dados de produto, sessões de usuário, conteúdo estático)
  • Adicionar cacheTag() a cada função cacheada que possa requerer invalidação sob demanda
  • Usar updateTag() para invalidação sob demanda em Server Actions
  • Testar em modo produção com next build && next start porque o comportamento do cache em next dev difere significativamente
  • Escrever testes Playwright com instant() para detectar regressões de navegação
  • Usar o Navigation Inspector nas DevTools para visualizar os shells prefetchados

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Fontes

O que lembrar sobre Cache Components no Next.js 16

  • Next.js 16 substitui o cache implícito por "use cache" explícito nos níveis de arquivo, componente e função
  • Next.js 16.3 adiciona Instant Navigations: com partialPrefetching: true, as aplicações ficam tão responsivas quanto SPAs
  • PPR é estável sob cacheComponents: true, entregando shells estáticos com conteúdo dinâmico em streaming
  • Os perfis cacheLife substituem revalidate com controle centralizado e tridimensional da duração do cache
  • cacheTag + updateTag habilitam a invalidação sob demanda; tags ausentes significam expiração apenas por tempo
  • "use cache: private" é obrigatório para dados de usuário para prevenir vazamentos de dados entre usuários
  • "use cache: remote" ajuda ambientes serverless a manter o cache entre cold starts
  • Root params de next/root-params resolvem o prop-drilling para segmentos dinâmicos globais como [lang]
  • As perguntas de entrevista em 2026 focam na mudança de implícito para explícito, Instant Navigations 16.3, Partial Prefetching e segurança do cache

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Desafio do dia

Você saberia encontrar o bug em React / Next.js?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador da SharpSkill

Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.

Atualizado em 25 de agosto de 2026

Tags

#next.js
#cache-components
#use-cache
#ppr
#interview
#react

Compartilhar

Artigos relacionados