Como o Turbopack decide quais pedaços de JavaScript seu usuário baixa

Abra a aba Network de uma aplicação Next.js e provavelmente vai aparecer uma coleção de arquivos JavaScript com nomes que parecem ter saído de um gerador de senhas. Aqueles arquivos são os chunks: pedaços do código da aplicação, das dependências e do runtime que o navegador baixa para montar cada página.

Separar o JavaScript em pedaços não é novidade. A parte interessante está em decidir o que entra em cada pedaço. Um chunk maior reduz o número de requisições, mas pode mandar código que aquela página não usa. Um chunk menor evita desperdício, porém aumenta o número de arquivos e pode reapresentar ao navegador código que ele já tinha baixado em outra combinação.

Em setembro, a equipe do Next.js publicou uma explicação detalhada de como o Turbopack faz essa escolha e apresentou opções experimentais do Next.js 16.3 para ajustar o processo. O material ajuda a enxergar chunking como um problema de tráfego e cache, não como uma caixa-preta que sempre melhora quando gera arquivos menores.

Um arquivo para tudo também tem um preço

A estratégia mais simples seria colocar todo o JavaScript da aplicação em um único arquivo. O primeiro acesso ficaria pesado, mas as páginas seguintes encontrariam esse bundle no cache. Isso pode funcionar em uma aplicação pequena. Conforme o projeto cresce, uma rota simples acaba pagando pelo editor de texto, pelo gráfico e pelo player de vídeo usados em áreas que o visitante talvez nunca abra.

No outro extremo, o bundler poderia gerar um arquivo para cada módulo. O navegador receberia somente os módulos necessários e reaproveitaria cada um deles entre as páginas. O problema muda de lugar: centenas de módulos viram centenas de requisições. HTTP/2 reduziu bastante o custo de fazer várias solicitações, mas esse custo não desapareceu. Arquivos minúsculos também aproveitam pior a compressão, porque há menos conteúdo repetido dentro de cada unidade.

Gerar um chunk por página parece um meio-termo, até dois caminhos diferentes usarem o mesmo componente. Se o rodapé estiver empacotado uma vez na página inicial e outra vez na documentação, o usuário baixa duas cópias do mesmo código ao navegar entre elas. O trabalho real do chunker é encontrar uma divisão que não exagere em nenhum desses três custos: bytes desnecessários, requisições demais e pouco reaproveitamento de cache.

O Turbopack pensa em grupos de chunks

O Turbopack organiza chunks em grupos carregados juntos. Uma rota forma um grupo; um import() dinâmico pode formar outro. Dentro de cada grupo, o bundler avalia quais arquivos pequenos vale a pena juntar. Como os itens daquele grupo já seriam necessários para a mesma navegação, a união reduz requisições sem acrescentar código estranho à página naquele primeiro momento.

A dificuldade aparece na navegação seguinte. Imagine que o chunk A contém um rodapé usado em todo o site e o chunk B contém um player usado somente na home. Juntar A e B economiza uma requisição quando a home abre. Se o visitante depois entra em uma página que precisa apenas do rodapé, o arquivo combinado não serve como substituto do A isolado. O navegador pode acabar baixando o código do rodapé outra vez.

Por isso, a decisão não depende apenas do tamanho de cada módulo. O algoritmo considera em quantos grupos A aparece sozinho, em quantos B aparece sozinho e em quantos os dois aparecem juntos. Também tenta estimar a chance de a sessão terminar na primeira página ou continuar em outra rota. É uma heurística, não uma leitura do futuro.

Os números mostram por que não existe configuração perfeita

A equipe comparou três estratégias no próprio site do Next.js durante uma sequência de oito páginas. Sem juntar chunks, o navegador transferiu 561,6 KiB em 96 requisições. Com a configuração padrão do Turbopack, foram 554,8 KiB em 38 requisições. Ao juntar tudo o que fosse possível dentro de cada grupo, o número caiu para 15 requisições, mas o volume subiu para 610 KiB.

O padrão reduziu as requisições em mais da metade sem aumentar os bytes transferidos naquele percurso. Já a junção agressiva venceu no carregamento inicial e perdeu conforme a navegação avançou, porque combinações maiores foram menos reutilizáveis. Isso não prova que os mesmos números aparecerão em qualquer aplicação. É um teste feito pela equipe no próprio site, com uma sequência específica de rotas. O valor está em deixar o conflito visível.

Component chunks tentam aproveitar o que já está no cache

Uma das experiências do Next.js 16.3 é gerar, ao mesmo tempo, o chunk combinado e suas partes menores. O runtime acompanha quais componentes o navegador já carregou e escolhe a alternativa mais barata para a próxima rota: buscar o arquivo inteiro ou baixar somente as partes ausentes. Se o usuário já recebeu A dentro de A+B, não deveria precisar buscar A novamente.

A opção fica em experimental.turbopackChunking e vem desativada. Um recorte da configuração seria:

import type { NextConfig } from 'next'

const nextConfig = {
  experimental: {
    turbopackChunking: {
      generateComponentChunks: true,
      minComponentChunkSize: 20_000,
    },
  },
} satisfies NextConfig

export default nextConfig

O limite mínimo evita transformar cada função pequena em um arquivo separado. Component chunks menores do que esse valor são agrupados, reduzindo o risco de trocar duplicação de código por uma chuva de requisições. A equipe também está experimentando o modo only-if-cached da Fetch API para consultar o cache HTTP sem recorrer automaticamente à rede. Esse modo exige requisições same-origin e responde com 504 quando não encontra uma entrada, então não é um botão genérico de “usar cache”.

Dados reais podem substituir parte dos palpites

O chunker precisa assumir como as pessoas navegam. A configuração padrão atribui peso de 0,67 ao primeiro carregamento, como se aproximadamente dois terços das sessões terminassem em uma página. É uma referência razoável para começar, mas não descreve igualmente um blog, um painel interno e um sistema usado durante oito horas por dia.

As novas opções permitem informar essa realidade ao algoritmo. firstPageLoadPriority muda o peso dado ao primeiro acesso; a própria documentação sugere a taxa de rejeição do site como aproximação. priorityRoutes identifica páginas cujo carregamento inicial merece mais atenção. clusters reúne rotas que costumam aparecer na mesma sessão, permitindo que o Turbopack seja mais agressivo ao combinar código compartilhado entre elas.

Isso cria uma ponte interessante entre RUM e build. Em vez de escolher a estratégia apenas pela intuição do time, dá para observar as rotas de entrada e os caminhos mais comuns. Ainda assim, copiar a taxa de rejeição do Analytics para a configuração e encerrar o assunto seria otimização por procuração. O que importa é medir o resultado entregue: bytes transferidos, número de requisições, acertos de cache e tempo de interação nas navegações reais.

Mandar menos código continua sendo melhor do que repartir desperdício

Chunking distribui o JavaScript que existe. Ele não corrige sozinho uma dependência pesada enviada ao cliente sem necessidade. As mudanças apresentadas também atacam esse lado: tree shaking experimental de módulos CommonJS, um runtime compartilhado entre páginas e um runtime padrão mais leve, que só inclui suporte a WebAssembly e Web Workers quando a aplicação realmente usa esses recursos.

Essa distinção evita uma armadilha comum. Uma aplicação pode exibir muitos arquivos pequenos na aba Network e ainda enviar pouco JavaScript; outra pode mostrar poucos arquivos e transferir centenas de kilobytes desnecessários. Contar chunks sem olhar tamanho, conteúdo e sequência de navegação diz pouco.

Vale mexer na configuração agora?

O Turbopack é o bundler padrão do Next.js desde a versão 16, mas isso não torna estáveis todas as opções ao redor dele. A documentação classifica experimental.turbopackChunking como experimental, sujeita a mudanças e não recomendada para produção. É um bom laboratório para quem consegue comparar builds e aceitar que nomes ou comportamentos podem mudar. Não é uma correção para aplicar às cegas porque apareceu uma nova chave no next.config.ts.

Antes de ajustar limites ou heurísticas, vale seguir uma sequência simples:

  • identifique quais rotas realmente enviam JavaScript demais;
  • use o Bundle Analyzer do Turbopack para encontrar dependências e cadeias de importação pesadas;
  • grave um percurso representativo no DevTools, incluindo navegações internas;
  • compare bytes, requisições e cache com a configuração padrão;
  • teste uma mudança por vez em ambiente controlado;
  • acompanhe métricas de usuários reais antes de transformar o experimento em padrão do projeto.

A parte mais útil desse trabalho do Turbopack talvez não seja uma opção específica. É a lembrança de que “bundle menor” e “site mais rápido” não são sinônimos automáticos. O navegador precisa baixar, descompactar, interpretar e executar arquivos enquanto o usuário percorre caminhos que o build só consegue estimar. Uma boa divisão de chunks reduz esse custo ao longo da sessão, não apenas na captura bonita da primeira página.

Fontes