JavaScript SEO: cómo indexa Google las SPA y los sitios en React

La cola de renderizado en dos oleadas de Google, las trampas del CSR, la diferencia entre SSR, SSG e ISR y métodos prácticos para comprobar que tu sitio con JavaScript se indexa correctamente.

Cómo renderiza Google JavaScript: cómo funciona

En una página HTML normal, Google solo da un paso: descarga el HTML y ve el contenido de inmediato. En una página con JavaScript hay tres pasos: descargar el HTML, cargar el paquete JS y ejecutarlo en Chromium sin interfaz gráfica para obtener el DOM final.

Google usa su propio motor de navegador basado en Chromium, que se actualiza con regularidad. Por eso Googlebot entiende las funciones modernas de JavaScript (ES2020+, import dinámico, WebAssembly). El problema no es el soporte de sintaxis, sino el retraso del renderizado y las limitaciones de recursos durante el rastreo.

Tres estrategias de renderizado de JavaScript: SSR y SSG/ISR entregan HTML listo, mientras que CSR construye el DOM en el navegador y acaba en la segunda oleada de indexación.

Renderizado en cifras

3–4 s

Tiempo límite de renderizado

Si el JS no se ejecuta en unos 3–4 segundos, Google indexa una página renderizada solo en parte

≈ 150 ms

Tiempo de CSS

Los recursos que tardan más de 150 ms en cargarse pueden bloquear el renderizado y perjudicar la eficiencia del rastreo

2 oleadas

Indexación

El contenido JS llega al índice con retraso respecto al contenido HTML: a veces, días o semanas

100 %

Google siempre actualizado

Googlebot se actualiza a la última versión estable de Chromium: las funciones modernas de JS son totalmente compatibles

Indexación en dos oleadas: por qué el contenido JS llega más tarde al índice

Google divide el proceso en dos etapas. La primera oleada es el rastreo: Googlebot descarga el HTML y todos los enlaces que descubre. La segunda oleada es el renderizado: el bot pone las páginas en cola para renderizarlas, ejecuta el JS e indexa el DOM final.

Entre una oleada y otra pueden pasar desde horas hasta semanas, según la autoridad del sitio, el presupuesto de rastreo y el tamaño de la cola de renderizado de Google. Si tus textos clave, los H1, las metaetiquetas o los enlaces se generan solo con JS, llegarán al índice con retraso.

Conclusión práctica: todo el contenido crítico para el SEO (la etiqueta title, la meta descripción, el H1, los enlaces y los datos estructurados) debe estar en la respuesta HTML del servidor. Así entra en la primera oleada de indexación, sin retraso.
La indexación en dos oleadas es el motivo por el que las páginas nuevas de una SPA «desaparecen» del índice durante 2–4 semanas tras publicarse, aunque el HTML sea accesible.

CSR: la principal trampa para el SEO

El renderizado en el cliente (CSR) consiste en que el servidor envía un <div id="root"></div> vacío y todo el HTML se construye en el navegador tras cargar el paquete JS. React, Vue y Angular en modo SPA funcionan así por defecto.

Desde el punto de vista del SEO, el CSR es un problema por varias razones: 1) el renderizado solo ocurre en la segunda oleada; 2) los recursos que el rastreador tiene bloqueados (scripts de terceros, CDN bloqueadas en robots.txt) no se cargan y la página se renderiza de forma incompleta; 3) cualquier error de JS deja la página en blanco en el índice.

html
<!-- 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>
Error crítico: generar <title>, <meta name="description"> y <h1> solo con JavaScript en el cliente. Google recibe valores vacíos o por defecto durante el primer rastreo e indexa la página sin ellos.

SSR, SSG e ISR: cuál elegir para el SEO

La solución a los problemas del CSR es generar el HTML en el servidor. Hay tres enfoques principales, y cada uno es óptimo para su escenario.

Tres enfoques de renderizado de JavaScript y su impacto en el SEO.

Comparativa de enfoques de renderizado

EnfoqueHTML en el servidorSEODatos dinámicos
CSRNoDeficienteTotal
SSRSí (en cada petición)ExcelenteAlta
SSGSí (en la compilación)ExcelenteNo (requiere recompilar)
ISRSí (por temporizador)ExcelenteMediante revalidación
Enfoques de renderizado y casos de uso adecuados:
EnfoqueIdeal para
CSRSPA autenticadas, paneles de control
SSRPáginas personalizadas, catálogos
SSGBlog, páginas de destino, documentación
ISRCatálogos de precios, noticias

SSR (Server-Side Rendering): el HTML se genera en el servidor en cada petición. Ofrece la máxima actualidad de los datos, pero aumenta la carga del servidor. En Next.js, generateMetadata y los componentes async del App Router aportan SSR por defecto.

SSG (Static Site Generation): el HTML se genera una sola vez, al compilar, y se sirve como archivo estático. Tiene el TTFB más bajo y es ideal para el SEO. Inconveniente: si cambian los datos, hay que recompilar.

ISR (Incremental Static Regeneration): un enfoque híbrido. Las páginas se compilan de forma estática, pero tras un periodo de revalidación (revalidate: 3600), el servidor las regenera en segundo plano con la siguiente petición. En Next.js es el enfoque estándar para la mayoría de las páginas.

typescript
// 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>
  );
}

Enlaces dinámicos y navegación

Googlebot solo entiende los enlaces en formato <a href="/url">. Los enlaces implementados con onClick, button, data-href o window.location son invisibles para el rastreador. Por eso, las páginas a las que solo se llega por esa navegación pueden no indexarse.

  • Usa <a href> en todos los enlaces de navegación, no button onClick con router.push()
  • Paginación mediante parámetros de URL (?page=2): Googlebot rastrea estas URL si aparecen en el HTML
  • Router de SPA: el <Link> de Next.js y el <Link> de React Router generan etiquetas <a> correctas en el servidor
  • La navegación por hash (#ancla) no crea URL separadas en el índice: no la uses para contenido que deba posicionarse por sí mismo
  • Menús dinámicos: si los elementos del menú se cargan desde una API tras la carga de la página, Google puede no descubrirlos hasta el renderizado
Comprobación: en Google Search Console → Inspección de URL, 'Ver como Googlebot' muestra qué enlaces ha encontrado el rastreador en la página. Si un enlace dinámico no aparece en la lista, Googlebot no puede verlo.

Carga diferida y scroll infinito

Googlebot carga las imágenes con loading="lazy": el rastreador moderno admite la carga diferida. Pero hay una condición: la imagen debe estar en el DOM con un atributo src correcto durante el SSR. Si el src lo asigna JavaScript en el navegador, el rastreador ve una etiqueta vacía.

El scroll infinito necesita una ruta rastreable hasta cada elemento relevante. Ofrece URL secuenciales y enlaces normales que funcionen sin que el usuario pulse ningún botón. Después, usa la Inspección de URL o un rastreo para comprobar que el HTML renderizado expone el contenido previsto.

Patrón¿Lo ve Googlebot?Recomendación
<img src="/img.webp" loading="lazy">SíÚsalo
<img data-src="/img.webp"> (el JS asigna src)NoEvítalo
Scroll infinito sin URLSolo la primera pantallaAñade paginación
Contenido tras el botón 'Mostrar más'NoMuestra una parte en el HTML
Pestañas con contenido ocultoSí, con retrasoDuplícalo en el HTML

Diagnóstico: cómo comprobar el renderizado de JavaScript

La pregunta clave del diagnóstico es: «¿qué ve Googlebot en mi página?». Hay varias herramientas para responderla.

Herramientas de diagnóstico

  • Google Search Console → Inspección de URL: muestra una captura de la página tras el renderizado de Googlebot, la lista de recursos y los enlaces descubiertos. Es el método de verificación más directo.
  • 'Ver como Googlebot' en GSC: ejecuta el rastreador real y devuelve el HTML tras el renderizado. Compáralo con el HTML fuente.
  • curl o wget: obtienen el HTML sin procesar, sin JS. Si las metaetiquetas clave y el contenido aparecen en la salida de curl, entrarán en la primera oleada de indexación.
  • Desactivar JS en las DevTools de Chrome (F12 → Ctrl+Shift+P → 'Disable JavaScript'): una forma rápida de ver la página como la vería el rastreador, sin renderizado.
  • Screaming Frog con renderizado: con la opción 'Spider > Configuration > Rendering > Googlebot' puedes rastrear el sitio con renderizado de JS y ver los enlaces encontrados tras el renderizado.
Ejemplo de configuración de Next.js para SSR/SSG:
bash
# 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'

Lista de comprobación de JavaScript SEO

Comprobaciones clave

  • El title y la meta descripción están en la respuesta HTML del servidor (SSR/SSG), no los genera el JS
  • El H1, los H2 y el texto principal son accesibles sin JavaScript
  • Los enlaces de navegación usan <a href>, no onClick ni button
  • Los datos estructurados (JSON-LD) están en el <head> de la respuesta HTML
  • Las imágenes tienen un src estático en el HTML; loading="lazy" solo en las que no son LCP
  • El robots.txt no bloquea los archivos JS/CSS necesarios para el renderizado
  • Ningún error de JS rompe el renderizado (compruébalo en la consola de Chrome DevTools de la página)
  • Core Web Vitals: LCP, INP y CLS cumplen los valores umbral de Google
  • La paginación se hace mediante parámetros de URL, no solo mediante el estado de JS
  • Las etiquetas canonical (<link rel="[canonical](/glossary/canonical)">) están en el HTML, no se asignan con JS

Preguntas frecuentes

¿Puede Google indexar JavaScript?
Sí. Googlebot usa Chromium Evergreen y puede ejecutar JS moderno. El problema no es el soporte, sino el retraso: el renderizado ocurre en la segunda oleada, que puede llegar días o semanas después del rastreo del HTML.
¿Un sitio en React o Next.js se indexa bien automáticamente?
Con Next.js y SSR/SSG, sí, si está bien configurado. Una SPA de React pura (Create React App, Vite sin SSR) es CSR por defecto, y eso es malo para el SEO. Lo clave es generar en el servidor las metaetiquetas, el H1 y el contenido principal.
¿Hace falta prerenderizado o renderizado dinámico?
El renderizado dinámico (mostrar HTML distinto a Googlebot que a los usuarios) es un enfoque obsoleto. Google lo permite, pero no lo recomienda. La vía correcta es SSR o SSG, que sirven el mismo HTML completo a todos.
¿Puedo usar React.lazy y code splitting?
Sí. El code splitting con React.lazy e import dinámico mejora el rendimiento y no perjudica el SEO, siempre que el contenido crítico (H1 y texto principal) ya esté en el HTML inicial. Los componentes que se cargan bajo demanda los seguirá obteniendo Googlebot durante el renderizado.
¿Cómo compruebo que mi sitio se indexa correctamente?
Usa Google Search Console → Inspección de URL. Solicita el renderizado y compara la captura con lo que ve un usuario normal. Además, comprueba con curl que el title, el h1 y la meta descripción están en la respuesta HTML sin JS.

Check it with SpiderHead

Free and local: run the audit on your own machine.

Try it