Arquiteto de Ilhas Astro
Astro é "envie HTML por padrão, hidrate apenas o que precisa de JS." Para sites de marketing, documentação e focados em conteúdo, isso produz bundles drasticamente menores que React/Next.js. O jogo inteiro é escolher quais componentes são estáticos e quais são ilhas.
Quando usar
- Site de marketing, blog, portal de documentação — o conteúdo é o produto.
- Migrando de um site de marketing em Next.js que está pesado em JS do lado do cliente.
- Multi-framework — uma biblioteca de componentes React usada junto com componentes Vue ou Svelte.
- Criação de conteúdo pesado com MDX + frontmatter type-safe.
- Transições de visualização entre páginas sem um SPA.
Capacidades principais
Estrutura de página
---
// src/pages/blog/[slug].astro — frontmatter executa em tempo de build/SSR
import { getCollection, getEntry } from 'astro:content';
import Layout from '../../layouts/Default.astro';
import Newsletter from '../../components/Newsletter.tsx';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((p) => ({ params: { slug: p.slug }, props: { post: p } }));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<Layout title={post.data.title}>
<h1>{post.data.title}</h1>
<article><Content /></article>
{/* Ilha — apenas este componente envia JS */}
<Newsletter client:visible />
</Layout>
As cercas --- são código do servidor. Dentro de <...>, o padrão é HTML estático. Componentes com diretivas client:* se tornam ilhas.
Diretivas de cliente — escolha a correta
| Diretiva | Quando hidrata | Uso para |
|---|---|---|
client:load | Carregamento da página | Interativos acima da dobra (carrinho, busca). |
client:idle | Quando o navegador está ocioso | Interativos não críticos (widget de chat). |
client:visible | Quando rolado para a viewport | Abaixo da dobra (assinatura de newsletter, comentários). |
client:media="(min-width: 768px)" | Quando a media query corresponde | Widgets apenas para desktop. |
client:only="react" | Pular SSR; renderizar apenas no cliente | Componentes que precisam de APIs do navegador na montagem. |
client:idle é o padrão correto para a maioria das ilhas não críticas. client:load é o martelo pesado; reserve-o.
Coleções de conteúdo
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content', // arquivos markdown/MDX
schema: z.object({
title: z.string().max(120),
description: z.string().max(160),
date: z.coerce.date(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
cover: z.object({ src: z.string(), alt: z.string() }).optional(),
}),
});
export const collections = { blog };
Agora getCollection('blog') é totalmente tipado. Erros de frontmatter quebram o build, não a produção.
const posts = (await getCollection('blog'))
.filter((p) => !p.data.draft)
.sort((a, b) => +b.data.date - +a.data.date);
Multi-framework
npx astro add react vue svelte
---
import ReactSearch from '../components/Search.tsx';
import VueChart from '../components/Chart.vue';
import SvelteToggle from '../components/Toggle.svelte';
---
<ReactSearch client:idle />
<VueChart client:visible />
<SvelteToggle client:load />
O runtime de cada framework é empacotado separadamente e carregado apenas nas páginas que o utilizam. Para um site de documentação que é majoritariamente estático, isso pode significar enviar zero JS do React para a maioria das páginas.
Ilhas de servidor (Astro 4+)
<Layout>
<Header />
<PersonalizedRecommendations server:defer>
<p slot="fallback">Carregando recomendações...</p>
</PersonalizedRecommendations>
<Footer />
</Layout>
server:defer renderiza o resto da página imediatamente e transmite o HTML do componente adiado em uma requisição separada. Útil para conteúdo personalizado que, de outra forma, forçaria a página inteira a ser SSR.
Otimização de imagens
---
import { Image } from 'astro:assets';
import cover from '../assets/cover.jpg';
---
<Image src={cover} alt="Foto de capa" widths={[400, 800, 1200]} sizes="(max-width: 768px) 400px, 1200px" />
Astro gera srcset, executa Sharp para redimensionamento e envia apenas o necessário. Coloque imagens sob src/assets/ para otimização completa; public/ não é processado.
Transições de visualização
---
import { ViewTransitions } from 'astro:transitions';
---
<html>
<head>
<ViewTransitions />
</head>
...
</html>
<a href="/about" transition:name="hero">Sobre</a>
Transições entre páginas sem um SPA. O navegador faz a maior parte do trabalho; Astro coordena a correspondência de elementos.
MDX com componentes
---
title: Meu post
date: 2026-04-30
---
import Callout from '../../components/Callout.astro';
# {frontmatter.title}
<Callout type="warn">
Isso é um aviso renderizado no servidor. Sem JS.
</Callout>
Componentes importados em MDX executam em tempo de build/SSR a menos que sejam ilhas.
SSR híbrido
// astro.config.mjs
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
output: 'hybrid',
adapter: cloudflare(),
});
---
// src/pages/api/search.ts — executa na borda
export const prerender = false;
export async function GET({ request }) {
const url = new URL(request.url);
const q = url.searchParams.get('q') ?? '';
const results = await search(q);
return new Response(JSON.stringify(results), { headers: { 'content-type': 'application/json' } });
}
prerender = false opta uma rota por SSR; o resto do site permanece estático.
Anti-padrões
client:load em todo lugar
Sintoma: O bundle JS do Lighthouse incha; a história de hidratação do Astro desaparece.
Diagnóstico: Engenheiros usam client:load por padrão porque é o mais familiar.
Correção: Use client:idle ou client:visible como padrão. Reserve client:load para interativos acima da dobra.
Buscar dados em componentes do cliente
Sintoma: Spinners de carregamento em todas as páginas; SEO prejudicado.
Diagnóstico: A busca de dados foi movida para componentes React em vez do frontmatter do Astro.
Correção: Busque nas cercas ---, renderize no servidor, passe dados como props para as ilhas.
Frontmatter sem tipo
Sintoma: Build bem-sucedido; produção mostra undefined em títulos.
Diagnóstico: Nenhum esquema de coleção de conteúdo; erro de digitação no frontmatter passou despercebido.
Correção: Use defineCollection com um esquema zod. Erros de frontmatter quebram o build.
Misturar public/ e src/assets/ para imagens
Sintoma: Algumas imagens otimizadas, outras são enviadas no tamanho original.
Diagnóstico: public/ é servido como está; apenas src/assets/ passa pelo Sharp.
Correção: Mova imagens para src/assets/. Use <Image> em todos os lugares. public/ apenas para imagens OG e favicons.
Tentar usar Astro para um SPA
Sintoma: Batalha constante com hidratação; ilhas crescem até que a página inteira se torne interativa. Diagnóstico: Framework errado para o trabalho. Astro brilha para conteúdo; SPAs precisam de Next/Remix. Correção: Use Next/Remix quando a maioria das páginas for interativa. Use Astro para marketing + documentação.
Uso excessivo de client:only
Sintoma: Mudança de layout (CLS) em cada carregamento de página; "piscada" antes do conteúdo aparecer.
Diagnóstico: Pular SSR significa que o navegador vê um placeholder vazio até o JS executar.
Correção: Use client:only apenas quando o componente realmente não puder ser renderizado no servidor. Forneça um placeholder que se aproxime do tamanho final.
Portões de qualidade
- A diretiva de cliente padrão é
client:idleouclient:visible;client:loadjustificado por uso. - Todo conteúdo tem um esquema tipado via
defineCollection. - Imagens estão sob
src/assets/e usam o componente<Image>. - Integrações multi-framework (
@astrojs/react, etc.) adicionadas apenas para componentes que existem. - LCP abaixo de 2s na página representativa mais lenta.
- Orçamento de JS do primeiro carregamento definido por página; CI falha em regressões.
- MDX usa componentes Astro para partes estáticas; ilhas apenas onde interativo.
- Transições de visualização testadas nos principais caminhos de navegação.
NÃO usar para
- SPAs completos — Next.js, Remix, TanStack Start são mais adequados.
- Criação de plugins/integrações Astro — skill separado (ainda não há skill dedicado).
- Ajustes de build do lado do Vite — Astro usa Vite internamente. →
vite-build-optimizerpara tamanho de chunks, HMR, problemas de ciclo de vida de plugins. - Apps com estado pesado do cliente (dashboards, ferramentas em tempo real) — JS demais para ilhas ajudarem.
- Mobile nativo — Astro é apenas web.