NAME
nextjs-16-cache-components — Next.js 16, Cache Components e React Compiler: o que realmente muda
SYNOPSIS
cat nextjs-16-cache-components.md
DESCRIPTION
Durante anos, uma das perguntas mais irritantes no Next.js foi: “Esta página é estática ou dinâmica?”. Parece uma pergunta simples, até você adicionar uma chamada para cookies(), um fetch com opções diferentes, um cliente de banco de dados, um CMS, um carrinho de compras ou um conteúdo personalizado.
Next.js 16 é interessante porque tenta tornar essa conversa menos misteriosa. Isso não elimina a complexidade, mas muda o modelo mental: as rotas são dinâmicas por padrão, o cache se declara onde necessário e Suspense se torna a maneira natural de compor shells rápidos com partes que permanecem atualizadas.
O recurso a ser entendido são os componentes de cache. Stable Turbopack, React Compiler, proxy.ts e as novas APIs de invalidação são importantes, mas giram em torno do mesmo problema: construir aplicativos rápidos sem ter que adivinhar o que a estrutura decidiu nos bastidores.
Porque essa coisa importa
Em um aplicativo real você não tem apenas “páginas estáticas” e “páginas dinâmicas”. Você tem peças diferentes com necessidades diferentes.
A ficha do produto pode mudar algumas vezes ao dia. O preço pode mudar com mais frequência. A disponibilidade deve estar quase ativa. O nome de usuário é pessoal. As avaliações podem ser transmitidas. A barra lateral pode ser estável. O carrinho não.
Se você tratar tudo como uma unidade, sempre terminará em um de dois extremos:
- cache agressivo e risco de ver dados antigos;
- renderização dinâmica em todos os lugares e desempenho pior que o necessário.
Cache Components serve justamente para evitar essa falsa escolha.
O modelo na prática
Com cacheComponents: true, você pode declarar o que pode ser armazenado em cache usando "use cache". Então você pode associar duração e tags a cacheLife() e cacheTag(). As partes dinâmicas permanecem dinâmicas e podem ser isoladas com Suspense.
A configuração é pequena:
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { cacheComponents: true, }; export default nextConfig;
A grande mudança não está na configuração. É como você começa a escrever os componentes.
// app/products/[slug]/page.tsx import { Suspense } from 'react'; import { cacheLife, cacheTag } from 'next/cache'; async function getProduct(slug: string) { 'use cache'; cacheLife('hours'); cacheTag(`product:${slug}`); return db.product.findUnique({ where: { slug } }); } async function ProductDetails({ slug }: { slug: string }) { const product = await getProduct(slug); return ( <section> <h1>{product.name}</h1> <p>{product.description}</p> </section> ); } async function LiveInventory({ slug }: { slug: string }) { const inventory = await db.inventory.findFirst({ where: { slug } }); return <p>{inventory.quantity} pezzi disponibili</p>; } export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params; return ( <> <ProductDetails slug={slug} /> <Suspense fallback={<p>Controllo disponibilità...</p>}> <LiveInventory slug={slug} /> </Suspense> </> ); }
A página não precisa estar toda armazenada em cache ou totalmente dinâmica. A ficha do produto pode ser rápida e reutilizável. O estoque pode permanecer atualizado. O usuário vê algo imediatamente, sem esperar pela parte mais lenta.
use cache é documentação executável
O que eu gosto em "use cache" é que ele força você a deixar uma intenção explícita. Ao ler uma função, você imediatamente entende que alguém decidiu: “esses dados podem ser reutilizados”.
É especialmente útil quando você não está usando fetch. Muitos aplicativos leem dados do Prisma, Drizzle, SDKs internos, clientes CMS ou funções de serviço. Nesses casos o antigo raciocínio baseado apenas nas opções fetch não era suficiente.
Uma regra prática:
- conteúdo de cachea relativamente estável;
- use tags granulares;
- deixa permissões dinâmicas, sessões, carrinhos, notificações e estados transacionais;
- coloque partes lentas dentro de
Suspense; - medir antes de dizer "melhoramos o desempenho".
Invalidar sem jogar tudo fora
O cache só será útil se você puder atualizá-lo com precisão. Aqui cacheTag, revalidateTag e updateTag tornam-se importantes.
Exemplo:
'use server'; import { updateTag } from 'next/cache'; export async function updateProductName(productId: string, name: string) { await db.product.update({ where: { id: productId }, data: { name }, }); updateTag(`product:${productId}`); }
O detalhe importante é a tag. product:${productId} informa um limite preciso. products conta um balde enorme. A princípio, o enorme balde é confortável; depois de alguns meses, esse é o motivo pelo qual você invalida metade de um aplicativo para alterar um título.
Stable Turbopack: a parte que você ouve todos os dias
Next.js 16 traz o Turbopack para o centro de desenvolvimento e construção. Não é o recurso mais poético, mas é o que você sente enquanto trabalha: servidor que inicia mais cedo, atualização mais rápida, compilações que deixam de parecer uma pausa forçada para o café.
Dito isso, eu não migraria uma base de código cheia de plug-ins personalizados de olhos fechados. Eu verificaria:
- construção local;
- importação fora do padrão;
- MDX, SVG e CSS;
- Plug-ins do Webpack restantes;
- páginas críticas;
- diferenças nos tempos de construção.
Para novos projetos, eu começaria pelo padrão. Para os mais maduros, eu faria uma migração comedida.
Compilador React: remova o ruído, não o pensamento
React Compiler 1.0 é estável e Next.js 16 oferece suporte com reactCompiler. A promessa é reduzir muita memorização manual: menos memo, menos useMemo, menos useCallback usado "por segurança".
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { reactCompiler: true, }; export default nextConfig;
Eu não trataria isso como um pó mágico. O compilador ajuda quando o código segue bem as regras do React. Se os componentes tiverem efeitos colaterais estranhos, mutações ocultas ou ganchos mal utilizados, isso precisa ser corrigido primeiro.
A maneira saudável de experimentar:
- atualizar
eslint-plugin-react-hooks; - corrigir violações reais;
- habilite-o em uma área controlada;
- medir o tempo de construção e o comportamento;
- remova a memorização manual somente quando ela não for mais necessária.
O objetivo não é apagar todos os useMemo. O objetivo é parar de escrever memoizações preventivas porque temos medo de renderizar.
proxy.ts e o limite da rede
O antigo middleware.ts torna-se proxy.ts. É uma mudança de nome, mas faz sentido: esse arquivo fica no limite da solicitação, não é um middleware genérico tradicional no estilo back-end.
// proxy.ts import { NextRequest, NextResponse } from 'next/server'; export default function proxy(request: NextRequest) { const isLoggedIn = Boolean(request.cookies.get('session')); if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); }
A regra aqui é simples: mantenha o tamanho pequeno. Redirecionamento, roteamento de autenticação, cabeçalhos, reescritas essenciais. Se começar a parecer um segundo back-end, provavelmente está fazendo muito.
Como eu realmente migraria
Eu não ativaria todos os recursos de uma vez. Eu faria isso:
- Eu atualizo Next, React e React DOM;
- Eu lanço o codemod oficial;
- Corrijo alterações importantes em
params,searchParams,cookies(),headers()edraftMode(); - Eu migro
middleware.tsparaproxy.ts; - Verifico compilações e páginas críticas;
- Eu habilito os componentes de cache em uma seção onde o cache atualmente cria atrito;
- Defino convenções para tags e invalidação;
- Eu tento o React Compiler separadamente;
- comparação de métricas antes e depois.
A boa migração não é aquela que utiliza todos os novos recursos. É o que torna o comportamento do aplicativo mais legível.
O que muda na forma de pensar
A coisa mais útil sobre Next.js 16 é que ele força você a nomear melhor as intenções. Uma função não é apenas “obter o produto do banco de dados”. É "pegue o produto, posso armazená-lo em cache por horas, invalido-o com esta tag". Um componente não é apenas “renderizar a página”. É “este é o shell rápido, esta peça é pessoal, isso vem em streaming”.
A princípio parece mais trabalho. Então se torna uma forma de calma. As decisões de desempenho não estão mais escondidas em uma combinação de padrões, heurísticas e memória tribal. Eles estão no código.
Fontes úteis
METADATA
- date: 2026-05-24
- reading: 7 min
- author: Filippo Spinella
- tags: Next.js, React, Frontend, Performance