SEO para JavaScript: como o Google indexa SPAs e sites em React
A fila de renderização em duas ondas do Google, as armadilhas do CSR, a diferença entre SSR, SSG e ISR e métodos práticos para verificar se o seu site em JavaScript está indexado corretamente.
Como o Google renderiza JavaScript: como funciona
Em uma página HTML comum, o Google dá um passo: baixa o HTML e vê o conteúdo na hora. Em uma página com JavaScript são três passos: baixar o HTML → carregar o bundle JS → executar o código em um Chromium headless para obter o DOM final.
O Google usa o próprio motor de navegador, baseado no Chromium e mantido sempre atualizado. Por isso o JavaScript moderno (ES2020+, import dinâmico, WebAssembly) é entendido pelo Googlebot. O problema não é o suporte à sintaxe, e sim o atraso na renderização e as limitações de recursos durante o rastreamento.
Renderização em números
Tempo limite de renderização
Se o JS não for executado em cerca de 3–4 segundos, o Google indexa uma página parcialmente renderizada
Tempo de CSS
Recursos que demoram mais de 150 ms para carregar podem bloquear a renderização e prejudicar a eficiência do rastreamento
Indexação
O conteúdo em JS chega ao índice com atraso em relação ao conteúdo em HTML — às vezes dias, às vezes semanas
Evergreen do Google
O Googlebot é atualizado para a versão estável mais recente do Chromium — os recursos modernos de JS são totalmente suportados
Indexação em duas ondas: por que o conteúdo em JS chega ao índice depois
O Google divide o processo em duas etapas. A primeira onda é o rastreamento: o Googlebot baixa o HTML e todos os links encontrados. A segunda onda é a renderização: o bot coloca as páginas na fila de renderização, executa o JS e indexa o DOM final.
Entre uma onda e outra podem se passar de horas a semanas, dependendo da autoridade do site, do orçamento de rastreamento e do tamanho da fila de renderização do Google. Se os textos principais, os H1, as meta tags ou os links forem gerados só por JS, eles vão chegar ao índice com atraso.
CSR — a principal armadilha de SEO
A renderização no cliente (CSR) acontece quando o servidor envia um <div id="root"></div> vazio e todo o HTML é montado no navegador depois que o bundle JS carrega. React, Vue e Angular no modo SPA funcionam assim por padrão.
Do ponto de vista de SEO, o CSR é um problema por vários motivos: 1) a renderização só acontece na segunda onda; 2) recursos bloqueados para o rastreador (scripts de terceiros, CDNs com bloqueio no robots.txt) não carregam, e a página fica renderizada pela metade; 3) qualquer erro de JS significa página em branco no índice.
<!-- CSR: what Google sees in the first wave -->
<html>
<head>
<title></title> <!-- empty -->
</head>
<body>
<div id="root"></div> <!-- no content -->
<script src="/bundle.js"></script>
</body>
</html><title>, <meta name="description"> e <h1> apenas por JS no cliente. Na primeira rodada de rastreamento, o Google recebe valores vazios ou padrão e indexa a página sem eles.SSR, SSG e ISR: qual escolher para SEO
A solução para os problemas do CSR é gerar o HTML no servidor. Há três abordagens principais, cada uma ideal para um cenário.
Comparação das abordagens de renderização
| Abordagem | HTML no servidor | SEO | Dados dinâmicos |
|---|---|---|---|
| CSR | Não | Ruim | Total |
| SSR | Sim (a cada requisição) | Excelente | Alta |
| SSG | Sim (no build) | Excelente | Não (exige rebuild) |
| ISR | Sim (por temporizador) | Excelente | Via revalidação |
| Abordagem | Ideal para |
|---|---|
| CSR | SPAs autenticadas, dashboards |
| SSR | Páginas personalizadas, catálogos |
| SSG | Blog, landing pages, documentação |
| ISR | Catálogos de preços, notícias |
SSR (renderização no servidor) — o HTML é gerado no servidor a cada requisição. Dá o máximo de atualização dos dados, mas aumenta a carga do servidor. No Next.js, o generateMetadata e os componentes async do App Router já oferecem SSR por padrão.
SSG (geração de sites estáticos) — o HTML é gerado uma vez, no build, e servido como arquivo estático. Tem o TTFB mais rápido e é ideal para SEO. A desvantagem é que qualquer mudança nos dados exige um novo build.
ISR (regeneração estática incremental) — um modelo híbrido: as páginas são geradas de forma estática, mas, após um período de revalidação (revalidate: 3600), o servidor as regenera em segundo plano na requisição seguinte. No Next.js, essa é a abordagem padrão para a maioria das páginas.
// Next.js App Router — SSG + ISR
export const revalidate = 3600; // revalidate every hour
export async function generateMetadata(): Promise<Metadata> {
return {
title: 'Product Catalogue',
description: 'Full range with up-to-date prices',
};
}
// All critical content is server-rendered:
export default async function CatalogPage() {
const products = await fetchProducts(); // server fetch
return (
<main>
<h1>Product Catalogue</h1>
{products.map(p => <ProductCard key={p.id} {...p} />)}
</main>
);
}Links dinâmicos e navegação
O Googlebot entende links apenas no formato <a href="/url">. Links implementados com onClick, button, data-href ou window.location são invisíveis para o rastreador. Isso significa que páginas acessíveis só por esse tipo de navegação podem não ser indexadas.
- Use
<a href>em todos os links de navegação, e nãobutton onClickcomrouter.push() - Paginação por parâmetros de URL (
?page=2): o Googlebot rastreia essas URLs se elas aparecerem no HTML - Roteador de SPA: o
<Link>do Next.js e o<Link>do React Router geram tags<a>corretas no servidor - Navegação por hash (
#anchor) não cria URLs separadas no índice; não use para conteúdo que deva ranquear de forma independente - Menus dinâmicos: se os itens do menu são renderizados por uma API depois do carregamento da página, o Google pode não descobri-los antes da renderização
Lazy load e rolagem infinita
Imagens com loading="lazy" são carregadas pelo Googlebot: o rastreador moderno oferece suporte ao carregamento lento (lazy loading). Mas há uma condição: a imagem precisa estar no DOM com o atributo src correto durante o SSR. Se o src for definido pelo JavaScript no navegador, o rastreador verá uma tag vazia.
A rolagem infinita precisa de um caminho rastreável até cada item relevante. Ofereça URLs sequenciais e links comuns que funcionem sem que o usuário clique em um botão; depois, use a Inspeção de URL ou um rastreamento para verificar se o HTML renderizado expõe o conteúdo pretendido.
| Padrão | O Googlebot vê? | Recomendação |
|---|---|---|
| <img src="/img.webp" loading="lazy"> | Sim | Use |
| <img data-src="/img.webp"> (JS define o src) | Não | Evite |
| Rolagem infinita sem URL | Só a primeira tela | Adicione paginação |
| Conteúdo atrás do botão ‘Mostrar mais’ | Não | Mostre parte no HTML |
| Conteúdo em abas (abas ocultas) | Sim, com atraso | Duplique no HTML |
Diagnóstico: como verificar a renderização do JavaScript
A pergunta-chave do diagnóstico é: ‘O que o Googlebot vê na minha página?’ Há várias ferramentas para responder a isso.
Ferramentas de diagnóstico
- Google Search Console → Inspeção de URL: mostra uma captura de tela da página depois da renderização pelo Googlebot, a lista de recursos e os links descobertos. É o método de verificação mais direto.
- ‘Ver como o Googlebot’ no GSC: executa o rastreador de verdade e devolve o HTML depois da renderização. Compare com o HTML de origem.
curlouwget: obtêm o HTML bruto, sem JS. Se as principais meta tags e o conteúdo estiverem na saída docurl, eles vão estar na primeira onda de indexação.- Desative o JS no Chrome DevTools (F12 → Ctrl+Shift+P → ‘Disable JavaScript’): uma forma rápida de ver a página como o rastreador a veria, sem renderização.
- Screaming Frog com renderização: a configuração ‘Spider > Configuration > Rendering > Googlebot’ permite rastrear o site com renderização de JS e ver os links encontrados depois da renderização.
# Check what Google sees without JS rendering:
curl -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
https://example.com/page
# Find all title / meta description / h1 in HTML response:
curl -s https://example.com/page | grep -E '<title>|<meta name="description"|<h1'Checklist de SEO para JavaScript
Verificações principais
- Title e meta description estão presentes na resposta HTML do servidor (SSR/SSG), e não são gerados por JS
- H1, H2 e o texto principal são acessíveis sem JavaScript
- Links de navegação usam
<a href>, e nãoonClick/button - Dados estruturados (JSON-LD) estão no
<head>da resposta HTML - Imagens têm
srcestático no HTML;loading="lazy"só para imagens que não são o LCP - O robots.txt não bloqueia os arquivos JS/CSS necessários para a renderização
- Nenhum erro de JS quebra a renderização (verifique o Console do Chrome DevTools na página)
- Core Web Vitals: LCP, INP e CLS atingem os valores de referência do Google
- Paginação por parâmetros de URL, e não só pelo estado do JS
- URLs canônicas (
<link rel="[canonical](/glossary/canonical)">) no HTML, e não definidas via JS
FAQ
O Google consegue indexar JavaScript?
Um site em React/Next.js é indexado bem automaticamente?
Preciso de pré-renderização ou de renderização dinâmica?
Posso usar React.lazy e divisão de código (code splitting)?
React.lazy e import dinâmico melhora o desempenho e não prejudica o SEO, desde que o conteúdo crítico (H1, texto principal) já esteja no HTML inicial. Componentes carregados sob demanda ainda serão buscados pelo Googlebot durante a renderização.Como verifico se o meu site está indexado corretamente?
curl se o title, o h1 e a meta description estão no HTML da resposta, sem JS.Check it with SpiderHead
Free and local: run the audit on your own machine.



