Pular para o conteúdo
Zumkai

Next.js 16: o que quebra na migração

São 21 mudanças que quebram e o codemod oficial cobre cinco. O resto derruba o build sem aviso — incluindo uma que atinge quem usa scroll suave.

  • next.js
  • migração
Card com o número de mudanças que quebram no Next.js 16 e quantas o codemod oficial resolve.
Sumário
  1. Antes de começar: os mínimos
  2. O que o codemod resolve, e o que não
  3. Turbopack por padrão: seu build pode falhar de propósito
  4. Async Request APIs: acabou o período de graça
  5. middleware virou proxy — e o edge ficou para trás
  6. Cache: assinatura nova e uma flag que não é rename
  7. next/image: seis defaults mudaram
  8. O que foi removido de vez
  9. Três mudanças de tooling que passam despercebidas
  10. A mudança de scroll que quase ninguém viu
  11. O caminho que a Vercel recomenda: migrar com agente
  12. A ordem que eu seguiria
  13. Perguntas frequentes
  14. Fontes

O codemod oficial resolve cinco coisas. As outras dezesseis você faz na mão, e várias derrubam o build sem dar pista do motivo.

Não é uma atualização de rotina. O Next.js 16 encerrou o período de compatibilidade que a 15 tinha aberto, trocou o bundler padrão, renomeou o middleware, mudou seis defaults do next/image e removeu APIs que existiam desde sempre.

Este é o mapa do que quebra, na ordem em que você vai encontrar cada coisa.

Antes de começar: os mínimos

Três barreiras que param a instalação antes de qualquer código:

RequisitoMudança
Node.jsMínimo 20.9.0 (LTS). Node 18 não é mais suportado
TypeScriptMínimo 5.1.0
BrowsersChrome 111+, Edge 111+, Firefox 111+, Safari 16.4+

Se o seu deploy roda em Node 18, resolva isso primeiro — nada mais importa até lá. Quem segue a documentação de deploy com GitHub e Hostinger precisa conferir a versão de Node no painel antes de dar push.

O que o codemod resolve, e o que não

Comece por ele:

bash
npx @next/codemod@canary upgrade latest

Ele cobre exatamente cinco coisas: atualiza o next.config.js para a configuração nova do Turbopack, migra de next lint para o ESLint CLI, renomeia middleware para proxy, tira o prefixo unstable_ das APIs estabilizadas e remove o experimental_ppr de páginas e layouts.

O upgrade não roda todas as migrações. Se o seu app ainda usa acesso síncrono a Request APIs, rode também:

bash
npx @next/codemod@canary next-async-request-api .

E se você usava next lint, há um terceiro:

bash
npx @next/codemod@canary next-lint-to-eslint-cli .

Tudo que não está nessas três listas é trabalho manual.

Cobertura do codemod oficial na migração para o Next.js 16 O codemod upgrade resolve cinco mudanças; as demais exigem trabalho manual, incluindo defaults de imagem, remoções e mudanças de comportamento. O QUE O CODEMOD `upgrade` COBRE 5 o resto é manual Automático config do Turbopack · next lint → ESLint CLI · middleware → proxy · remoção de unstable_ · remoção de experimental_ppr Manual 6 defaults do next/image · default.js em rotas paralelas · assinatura do revalidateTag · edge no proxy · scroll-behavior · AMP · runtime config… Dois codemods extras existem: next-async-request-api e next-lint-to-eslint-cli
Fonte: guia oficial de upgrade para a versão 16, acesso em 20/08/2026.

Turbopack por padrão: seu build pode falhar de propósito

A partir da 16, o Turbopack é estável e usado por padrão em next dev e next build. Os flags --turbopack e --turbo viraram desnecessários.

O problema aparece em projeto com configuração de webpack: o build falha, deliberadamente, para evitar erro de configuração silencioso. Três saídas:

json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build --webpack"
  }
}

Usar --webpack mantém o comportamento antigo no build. Alternativamente, next build --turbopack ignora sua config de webpack, ou você migra a config para o equivalente em Turbopack.

Uma armadilha comum: se o build falha reclamando de config de webpack e você não escreveu nenhuma, provavelmente um plugin está injetando a opção.

Duas mudanças menores que pegam: a configuração saiu de experimental.turbopack para turbopack no topo do nextConfig, e o Turbopack não suporta o til (~) do Sass. @import '~bootstrap/...' vira @import 'bootstrap/...'.

Async Request APIs: acabou o período de graça

A versão 15 introduziu as Request APIs assíncronas com compatibilidade síncrona temporária. Na 16, o acesso síncrono foi removido.

Vale para cookies(), headers(), draftMode(), params em layout, page, route, default, opengraph-image, twitter-image, icon e apple-icon, e searchParams em page.

Há um detalhe que o codemod não cobre e que pega quem gera imagem dinâmica: nas funções de opengraph-image, twitter-image, icon e apple-icon, o id também virou Promise:

js
// Next.js 16
export default async function Image({ params, id }) {
  const { slug } = await params
  const imageId = await id   // agora é Promise<string>
}

O mesmo aconteceu com o id do sitemap. E note a assimetria: o generateImageMetadata continua recebendo params síncrono.

Para migrar com tipagem, npx next typegen gera os helpers PageProps, LayoutProps e RouteContext:

tsx
export default async function Page(props: PageProps<'/blog/[slug]'>) {
  const { slug } = await props.params
}

middleware virou proxy — e o edge ficou para trás

O arquivo middleware foi renomeado para proxy, para deixar clara a fronteira de rede. O export nomeado também muda:

ts
// proxy.ts
export function proxy(request: Request) {}

Flags de configuração acompanham: skipMiddlewareUrlNormalize virou skipProxyUrlNormalize.

A pegadinha que o rename esconde: o proxy não suporta o edge runtime. Ele roda em nodejs, e isso não é configurável. Quem depende de edge precisa continuar no middleware — a documentação promete instruções em release menor.

Ou seja: o rename esconde uma decisão de arquitetura, e o codemod vai renomear seu arquivo sem perguntar se você usa edge.

Cache: assinatura nova e uma flag que não é rename

Duas mudanças independentes, fáceis de confundir.

revalidateTag agora exige um segundo argumento com o perfil de cacheLife. A forma de um argumento está depreciada e produz erro de TypeScript:

ts
revalidateTag('posts')          // antes
revalidateTag('posts', 'max')   // agora

Se você precisa de expiração imediata em vez de stale-while-revalidate, existe a API nova updateTag, exclusiva de Server Actions, com semântica de read-your-own-writes: o usuário faz a mudança e vê o resultado na hora. Chamar fora de Server Action lança erro — em Route Handler e webhook, use revalidateTag com perfil.

Também estabilizaram cacheLife e cacheTag. Os imports com unstable_ podem cair.

A segunda mudança é maior do que parece. O flag experimental de Partial Prerendering saiu, junto com experimental.dynamicIO, experimental.useCache e o segment config experimental_ppr. O substituto é cacheComponents: true — e a documentação avisa que não é uma troca de nome:

Com a flag ligada, os route segment configs dynamic, revalidate e fetchCache passam a dar erro, substituídos por use cache e cacheLife. E há um detalhe que trava build sem escapatória: chamadas de IO síncrono no prerender (new Date(), Date.now(), Math.random(), crypto.randomUUID()) falham o build e não podem ser adiadas nem com a opção de escape instant = false.

Se você usa PPR hoje, a recomendação oficial é permanecer no canary da 15 que você já usa.

next/image: seis defaults mudaram

Nenhum quebra o build. Todos mudam o comportamento em produção.

ConfiguraçãoAntesAgora
minimumCacheTTL60 segundos4 horas (14400s)
imageSizesincluía 1616 removido do array padrão
qualitiestodas permitidas[75]
maximumRedirectsilimitado3
IP localpermitidobloqueado, salvo dangerouslyAllowLocalIP
Query string locallivreexige images.localPatterns.search

A de qualities é a que mais surpreende: se você passa quality={90}, o valor é coagido para o mais próximo da lista — ou seja, 75. Sua imagem fica com qualidade diferente da que você pediu, sem erro nenhum.

Além disso, next/legacy/image está depreciado e images.domains também — a substituição é images.remotePatterns.

O que foi removido de vez

RemovidoSubstituto
Suporte a AMP (next/amp, useAmp, amp config)Nenhum
Comando next lintBiome ou ESLint direto. next build não roda mais lint
serverRuntimeConfig e publicRuntimeConfigVariáveis de ambiente, com NEXT_PUBLIC_ para o cliente
devIndicators.appIsrStatus, buildActivity, buildActivityPositionO indicador em si continua
unstable_rootParamsnext/root-params

Duas mudanças estruturais no mesmo pacote: rotas paralelas agora exigem default.js explícito em cada slot, e sem ele o build falha; e o next build deixou de reportar as métricas size e First Load JS, porque a própria equipe as considerou imprecisas em arquitetura com Server Components.

Três mudanças de tooling que passam despercebidas

ESLint mudou de formato. O @next/eslint-plugin-next agora usa Flat Config por padrão, alinhado com o ESLint v10, que vai abandonar o formato legado. Se você ainda está no .eslintrc, a migração virou item de fila.

dev e build rodam ao mesmo tempo. Os dois passaram a usar diretórios de saída separados (o next dev escreve em .next/dev), e um lockfile impede duas instâncias do mesmo comando no mesmo projeto. Na prática, dá para deixar o dev server rodando enquanto valida um build.

process.argv não contém mais 'dev'. Antes, o arquivo de config era carregado duas vezes em desenvolvimento: no comando e no servidor. Agora carrega uma vez só, e a consequência é que checar process.argv.includes('dev') dentro do next.config retorna false.

Isso quebra plugin que dispara efeito colateral em desenvolvimento. A troca é direta:

js
// Antes — não funciona mais
const isDev = process.argv.includes('dev')

// Agora
const isDev = process.env.NODE_ENV === 'development'

Os comandos typegen e build continuam visíveis no process.argv. Só o dev sumiu.

A mudança de scroll que quase ninguém viu

Esta não aparece em lista de destaque e atinge todo site com motion.

Nas versões anteriores, se você tinha scroll-behavior: smooth no <html>, o Next.js sobrescrevia isso durante transição de rota: trocava para auto, navegava com salto instantâneo ao topo, e devolvia o valor original. Era o que mantinha a navegação entre páginas sensação de instantânea mesmo com scroll suave ativo.

Na 16, esse override acabou. O Next.js respeita o seu scroll-behavior.

O efeito prático: quem tem smooth scroll global vai ver a navegação de rota virar uma rolagem animada até o topo, em vez do salto seco. Em página longa, isso é perceptível e pode parecer bug.

Para recuperar o comportamento antigo, o opt-in é um atributo:

tsx
export default function RootLayout({ children }) {
  return (
    <html lang="pt-BR" data-scroll-behavior="smooth">
      <body>{children}</body>
    </html>
  )
}

Se o seu site depende de scroll suave, o catálogo de técnicas de motion para web cobre as alternativas.

Vale checar junto: se você habilitar cacheComponents, as rotas passam a ser preservadas com o <Activity> do React em modo oculto em vez de desmontadas. Isso significa que useState, valores de formulário e posição de scroll deixam de resetar ao navegar e voltar. Dropdown continua aberto, diálogo não refaz o efeito de foco, formulário mantém o resultado do submit.

O caminho que a Vercel recomenda: migrar com agente

A primeira seção do guia oficial de upgrade se chama "Use an AI agent (recommended)", e traz um prompt pronto para colar.

Isso não é marketing solto. A partir da 16.3, rodar next dev com um agente detectado no ambiente gera automaticamente AGENTS.md e CLAUDE.md na raiz do projeto. O CLAUDE.md gerado é literalmente uma linha:

md
@AGENTS.md

E o bloco gerenciado dentro do AGENTS.md abre com um aviso que vale ler:

O framework agora empacota a própria documentação dentro do pacote next, em node_modules/next/dist/docs/, com versão casada. Atualizar o Next.js atualiza os docs que o agente lê. Para desligar, agentRules: false.

Há uma afirmação nesse guia que interessa a quem monta setup de agente:

É a primeira medição pública que vi sobre esse trade-off, e ela contraria a intuição de "mova tudo para skill e deixe o contexto leve". Para conhecimento que o agente precisa em toda tarefa, a Vercel mediu que carregar sempre vence.

Vale ler junto com a anatomia de um CLAUDE.md que funciona: as duas fontes chegam ao mesmo lugar por caminhos opostos.

Também vale saber que existe um servidor MCP embutido em /_next/mcp, expondo rotas, logs e problemas de compilação do dev server, e uma skill oficial next-dev-loop para o ciclo de editar e verificar — ela entra na mesma categoria das que reuni no catálogo de skills avaliadas.

A ordem que eu seguiria

  1. Node 20.9 e TypeScript 5.1 no ambiente e no CI. Antes de tudo.
  2. Rode os três codemodsupgrade, next-async-request-api, next-lint-to-eslint-cli.
  3. Decida sobre o Turbopack antes de rodar o build. Se tem config de webpack, escolha entre migrar ou --webpack.
  4. Verifique o edge antes de aceitar o rename de proxy. É a decisão menos reversível da lista.
  5. next build e resolva o que aparecer. Rotas paralelas sem default.js aparecem aqui.
  6. Confira as seis mudanças do next/image em produção — nenhuma quebra o build, todas mudam o resultado.
  7. Teste a navegação com scroll suave, se você usa.
  8. cacheComponents fica para depois. É migração própria, não parte desta.

Os passos 1 a 5 são a migração. Os 6 a 8 são o que você descobre em produção se pular.

Perguntas frequentes

Dá para migrar sem adotar Cache Components?

Dá, e é o recomendado. cacheComponents é opcional na 16 e representa uma migração separada, com modelo de cache próprio. Se você usava experimental.dynamicIO ou experimental.useCache, remova os flags. Se usa PPR experimental hoje, a orientação oficial é ficar no canary da 15 até planejar a adoção.

O codemod é seguro para rodar direto na main?

Rode em branch. Ele renomeia arquivos, mexe no next.config e altera imports — e o rename de middleware para proxy é o caso mais delicado, porque muda o runtime disponível sem avisar.

Meu build falha reclamando de webpack e eu não uso webpack. Por quê?

Quase sempre é um plugin injetando a opção webpack na config. A documentação cita isso explicitamente. Ou você identifica o plugin, ou passa --webpack no build.

Preciso mesmo usar agente para migrar?

Não. O guia oferece o caminho manual completo. Mas se você já usa Claude Code ou Codex, vale rodar o next dev na 16.3+ só para ver o AGENTS.md sendo gerado — os docs empacotados por versão resolvem o problema de o agente responder com API da versão 14.

Fontes

Verificado em 20 de agosto de 2026.

Gatilho de reavaliação: revisar quando (a) sair a release menor com as instruções de edge runtime para proxy, (b) cacheComponents deixar de ser opcional, (c) os defaults do next/image mudarem de novo, ou (d) a geração automática de AGENTS.md mudar de comportamento.

Leia também