Pular para o conteúdo
upgbp

Por que este blog sobre Next.js roda em Astro

A escolha não foi ideológica: um site de conteúdo tem exigências diferentes das de um aplicativo. Os números deste site e onde o Next.js ganharia.

7 min de leitura

Ambiente testado

  • Astro 7.2
  • Tailwind CSS 4.3
  • Cloudflare Pages
  • Node 22
Neste artigo (12)

A pergunta que apareceu primeiro

A maior parte do que eu escrevo aqui trata de Next.js. Seria coerente que o site fosse feito em Next.js.

Não é. E a razão não é preferência — é que um blog e um aplicativo têm exigências diferentes o suficiente para justificar ferramentas diferentes.

O que um site de conteúdo precisa

Listando sem rodeio o que este site faz:

  • entrega texto e blocos de código;
  • precisa ser lido por um rastreador que não executa JavaScript com boa vontade;
  • tem exatamente três elementos interativos — alternar tema, abrir o menu no celular e destacar a seção atual no índice lateral;
  • não tem login, formulário, carrinho nem estado de sessão.

Nenhum framework de aplicação é ruim nisso. Só que todos carregam um runtime para resolver problemas que este site não tem.

Os números deste site

npm run build
Route                                          
├─ /                                            
├─ /artigos                                     
├─ /artigos/[slug]                              
├─ /categoria/[cat]                             
└─ /og/[slug].png            gerado em build     

JavaScript externo .......  0 arquivo, 0 KB
JavaScript inline ........  3,1 KB    (tema + índice do artigo)
CSS ......................  29,2 KB   (6791 bytes comprimido)
Fontes ...................  5 arquivos, 120 KB, self-hospedadas
HTML de um artigo ........  61 KB     (11,8 KB comprimido)

Zero arquivo de JavaScript. Não é otimização agressiva: é o padrão do Astro. Ele renderiza componentes em tempo de build e envia HTML. Interatividade é opcional, declarada componente a componente.

Os 3,1 KB inline são três coisas específicas: o script que resolve o tema antes da primeira pintura, o botão que alterna tema, e o IntersectionObserver que marca a seção atual no índice. Escrevi cada um deles de propósito.

Uma ressalva honesta: esses números são do código do site. O script de anúncios, quando ativo, é de terceiro, pesa muito mais que tudo acima somado e disputa a thread principal durante o carregamento. É justamente por saber disso que o resto é enxuto — o orçamento que sobra vai para o que não está sob meu controle.

O que a escolha entregou na prática

Realce de código sem custo no navegador

Os blocos de código deste artigo passaram pelo Shiki durante o build. O que chega ao navegador é HTML com cores embutidas — sem biblioteca de realce, sem trabalho na thread principal, e funcionando com JavaScript desligado.

O tema duplo é resolvido em CSS:

src/styles/global.css
/* O Shiki grava as duas cores em variáveis no próprio HTML.
   A troca de tema é uma regra de CSS, não uma re-renderização. */
[data-theme='dark'] .astro-code,
[data-theme='dark'] .astro-code span {
  color: var(--shiki-dark) !important;
  background-color: var(--shiki-dark-bg) !important;
}

O build recusa metadado ruim

Os artigos são arquivos MDX com frontmatter validado por um esquema. Isso não é enfeite: os dois campos que o Google exibe no resultado de busca têm limite, e estourar o limite custa clique.

src/content.config.ts
schema: z.object({
  // Acima de 70 caracteres o título é truncado no resultado de busca.
  title: z.string().min(15).max(70),
  // Faixa em que a descrição costuma ser exibida inteira.
  description: z.string().min(110).max(165),
  category: z.enum(['dados', 'apis', 'performance']),
  stack: z.array(z.string()).min(1),
  errorMessage: z.string().optional(),
  draft: z.boolean().default(true),
})

Um título longo demais quebra o build. É uma regra de SEO transformada em erro de compilação, que é o único lugar onde regra de SEO não é esquecida.

O draft: true por padrão significa que nenhum artigo vai ao ar por acidente — publicar exige uma edição explícita.

Fontes com métricas de fallback

O Astro 7 baixa a fonte durante o build, serve do próprio domínio e gera automaticamente uma fonte de fallback ajustada:

@font-face {
  font-family: 'IBM Plex Sans fallback: Arial';
  src: local('Arial');
  size-adjust: 99.5961%;
  ascent-override: 102.9157%;
  descent-override: 27.6115%;
}

Esses números fazem a fonte de sistema ocupar exatamente o mesmo espaço que a fonte real. Quando a troca acontece, nada se move — o texto aparece de imediato e o CLS fica em zero. Escrever isso à mão é possível e chato; ganhar de graça é melhor.

Cards de compartilhamento gerados no build

Um por artigo, com título, assunto, a mensagem de erro e a stack. Vinte e quatro artigos são vinte e quatro imagens que ninguém precisou desenhar.

Onde o Next.js ganharia

A comparação honesta precisa dos dois lados.

Necessidade Ferramenta que vence
Conteúdo majoritariamente estático Astro
Login, sessão, dados por usuário Next.js
Formulários com mutação e revalidação Next.js
Painel com muita interação Next.js
Ilhas pequenas de interatividade em páginas de conteúdo Astro
Ecossistema de bibliotecas React Next.js
Enviar o mínimo possível de JavaScript Astro

Se este site tivesse área de assinante, comentários próprios ou qualquer coisa com estado por usuário, a conta mudaria. Manter duas arquiteturas para juntar blog e aplicativo costuma custar mais do que aceitar um pouco de JavaScript extra nas páginas de conteúdo.

O que a escolha custou

Componentes React não vêm de graça. Dá para usar — o Astro suporta React, Vue, Svelte e outros — mas cada componente interativo carrega o runtime do framework dele. Um único componente React numa página tira o site do zero absoluto. Aqui, os três elementos interativos foram escritos sem framework justamente por isso.

Menos material de referência. Para cada problema de Astro existem dez respostas de Next.js. Quando aparece algo específico, você lê o código-fonte.

O Astro 7 mudou o processamento de Markdown. Ele trocou o remark/rehype por um processador nativo em Rust. É mais rápido e é estrito com HTML inválido — uma tag não fechada agora é erro de build. O efeito prático: praticamente todo tutorial de plugin de Markdown para Astro que você encontrar está desatualizado, e a adaptação é por conta própria.

Como confirmar por conta própria

Desligue o JavaScript. No DevTools, Command Palette → Disable JavaScript, e recarregue. Todo o conteúdo continua legível, os links funcionam, o realce de código permanece. O que se perde é o botão de tema e o destaque do índice — que é exatamente o esperado de um site cuja interatividade é opcional.

Veja o código-fonte. Ctrl+U em qualquer artigo mostra o texto inteiro no HTML. É assim que o rastreador vê a página, sem depender de execução de script.

Conte as requisições. Na aba Network, filtrando por domínio próprio, um artigo carrega o documento, um CSS e as fontes. Nenhum arquivo .js meu.

Se você for fazer o mesmo

Três decisões que renderam mais que as outras, para quem estiver montando algo parecido:

Valide o frontmatter com esquema. Regra de SEO que vive na cabeça de alguém é regra que some. Regra que quebra o build fica.

Rascunho como padrão. draft: true no esquema, e publicar exige ato consciente.

Escreva as poucas interações à mão. Três componentes interativos não justificam um framework de interface. Foram cerca de sessenta linhas de JavaScript no total, e elas são a diferença entre 3 KB e 90 KB.

Continue por aqui