Desarrollo

SEO en React y Angular: por qué el 40% de las SPAs no aparece en Google

David Morales Vega13 de diciembre de 202510 min de lectura

Has creado una Single Page Application (SPA) increíble con React o Angular. Es rápida, las transiciones son suaves y se siente como una app nativa. Pero cuando la lanzas, pasan las semanas y Google no indexa tu contenido.

Entras a ver qué pasa en Google Search Console y descubres que, para Googlebot, tu web es una página en blanco con un <div id="root"></div>. ¿Por qué?

El estado del JavaScript SEO en 2025: Los números

El problema de SEO en frameworks JavaScript no es anecdótico. Según estudios recientes:

Métrica Dato Impacto
Webs dependientes de JavaScript 67% Mayor necesidad de optimización
SPAs con baja indexación 40% Pérdida masiva de tráfico orgánico
Tráfico mobile-first indexado 78% Prioridad en rendimiento móvil
Aumento tráfico con SSR/SSG +35% Beneficio directo de optimización
Mejora conversión con render optimizado +18% Impacto en negocio
Mejora en búsqueda móvil con SSR +45% Ventaja competitiva

Caso real: E-commerce con 18,000 productos

Un retailer online usando React para su catálogo de productos enfrentó problemas severos de indexación:

Situación inicial:
─────────────────────────────────────
Productos totales:    18,000
Páginas indexadas:    4,140 (23%)
Bundle JavaScript:    2.3 MB
SSR implementado:     No
Resultado:            77% del catálogo invisible para Google

Después de implementar Server Side Rendering, la indexación subió al 94% en 6 semanas.

El conflicto: CSR (Client Side Rendering) vs. Arañas

Las webs tradicionales (PHP, WordPress, Rails) envían el HTML completo desde el servidor. La araña de Google llega, lee el texto en el HTML, indexa y se va.

En una SPA moderna creada con create-react-app o Angular CLI, el servidor envía un HTML casi vacío:

<!DOCTYPE html>
<html>
<head>
  <title>Mi App</title>
</head>
<body>
  <div id="root"></div>
  <script src="/bundle.js"></script>
</body>
</html>

Es el navegador del usuario el que descarga, lee y ejecuta ese JavaScript para "pintar" el contenido. Esto se llama Client Side Rendering (CSR).

Google Web Rendering Service (WRS): Limitaciones técnicas

Google usa el Web Rendering Service (WRS) para ejecutar JavaScript. Pero tiene limitaciones críticas:

1. Crawl Budget y Rendering Budget

Según documentación de Google (Diciembre 2024):

Recurso Impacto en Crawl Budget
HTML estático Mínimo (1x)
Página con JavaScript Alto (5-10x más recursos)
Bundle JS pesado (2MB+) Crítico (puede agotar budget)
Recursos externos (CDN) Consumen budget del host

Google asigna un presupuesto de rastreo a cada sitio. Ejecutar JavaScript consume significativamente más recursos que leer HTML.

2. Caché del WRS: 30 días

Según Martin Splitt y Gary Illyes de Google, el WRS cachea recursos JavaScript y CSS por hasta 30 días. Esto significa:

  • Si actualizas tu JS sin cache-busting, Google puede renderizar con versión vieja
  • Cambios en URLs de recursos consumen más crawl budget
  • Google recomienda usar fingerprinting para archivos JS

3. Latencia de APIs y timeout

Si tu contenido depende de una llamada a API (fetch('/api/products')), y esa API tarda más de unos segundos, Googlebot puede:

  • Ver un spinner de carga en lugar de tu contenido
  • Indexar la página incompleta
  • Abandonar el renderizado por timeout

4. Errores silenciosos

Si tu JavaScript tiene un error (un import que falla, una variable undefined), Google simplemente ve una página en blanco y no te avisa.

Impacto en métricas SEO reales

Estudios de 2024-2025 muestran el impacto directo:

Impacto del rendering budget excedido:
───────────────────────────────────────
Reducción en indexación:     -40%
Caída en tráfico orgánico:   -23%
Penalización Core Web Vitals: Significativa

Una startup que migró a React sin SSR vio cero indexación en Google. Después de implementar react-helmet-async para meta tags y pre-rendering, el tráfico orgánico aumentó 80% en 3 meses.

Core Web Vitals 2025: Las métricas que importan

En 2025, Google confirmó que Page Experience es factor de ranking. Las métricas Core Web Vitals actualizadas:

Métrica Qué mide Objetivo
LCP (Largest Contentful Paint) Velocidad de carga menos de 2.5 segundos
INP (Interaction to Next Paint) Responsividad (reemplazó FID en 2024) menos de 200ms
CLS (Cumulative Layout Shift) Estabilidad visual menos de 0.1

SPA vs SSR en Core Web Vitals

Framework LCP típico INP típico Problema principal
React SPA (CSR) 4-8s 300ms+ Bundle JS pesado
Angular CLI 3-6s 250ms+ Hydration lenta
Next.js (SSR) 1.5-2.5s 100-150ms Mínimo
Astro (SSG) 0.5-1.5s 50-100ms Casi ninguno

Con SSR, el HTML llega completo en el primer request, mejorando dramáticamente LCP. Con SPA pura, el navegador debe:

  1. Descargar HTML vacío
  2. Descargar bundle JS (a veces 2MB+)
  3. Parsear y ejecutar JS
  4. Hacer llamadas a APIs
  5. Renderizar contenido

La Solución: SSR, SSG e ISR

Para proyectos donde el SEO importa, no podemos confiar solo en el navegador. Tenemos tres arquitecturas de renderizado:

1. Server Side Rendering (SSR)

El servidor ejecuta tu código React/Angular y envía el HTML completo al navegador. Herramientas: Next.js, Angular Universal, Remix.

// Next.js App Router - SSR por defecto
async function ProductPage({ params }: { params: { id: string } }) {
  const product = await fetch(
    `https://api.example.com/products/${params.id}`,
    { cache: 'no-store' } // Fuerza SSR
  ).then(res => res.json())
  if (!product) {
    notFound()
  }
  return (
    <article>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <span>${product.price}</span>
    </article>
  )
}
export default ProductPage

Ventajas:

  • Google ve HTML completo al instante
  • Datos siempre actualizados (cada request es fresh)
  • SEO perfecto
  • Angular Universal mejora load time hasta 50%

Desventajas:

  • Mayor carga en el servidor (cada página se genera on-demand)
  • Latencia inicial más alta que SSG

Cuándo usarlo: E-commerce, dashboards con contenido personalizado, apps con datos en tiempo real.

2. Static Site Generation (SSG)

Generas todo el HTML en tiempo de build (cuando haces npm run build). El resultado son archivos HTML estáticos que sirves desde un CDN.

// Next.js App Router - SSG
export async function generateStaticParams() {
  const posts = await getAllPostsFromCMS()
  return posts.map(post => ({ slug: post.slug }))
}
async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)
  return <article>{post.content}</article>
}
export default BlogPost

Ventajas:

  • Velocidad extrema (HTML estático en CDN)
  • Costos mínimos de servidor
  • SEO perfecto
  • LCP típico: menos de 1.5s

Desventajas:

  • No sirve para contenido que cambia constantemente
  • Rebuild si actualizas contenido (mitigado con ISR)

Cuándo usarlo: Blogs, portfolios, landing pages, documentación.

3. Incremental Static Regeneration (ISR)

Lo mejor de ambos mundos: páginas estáticas que se regeneran en background cuando cambia el contenido.

// Next.js App Router - ISR
async function ProductsPage() {
  const products = await fetch('https://api.example.com/products', {
    next: { revalidate: 60 } // Regenera máximo cada 60 segundos
  }).then(res => res.json())
  return (
    <ul>
      {products.map(p => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  )
}

Con revalidate: 60, Next.js:

  1. Sirve la versión estática (súper rápida)
  2. En background, verifica si pasaron 60 segundos
  3. Si pasaron, regenera la página con datos frescos
  4. La próxima visita recibe la versión actualizada

Cuándo usarlo: E-commerce con inventario que no cambia cada segundo, noticias, sitios con contenido semi-dinámico.

Frameworks modernos y sus enfoques (2025)

Framework Renderizado LCP típico Mejor para
Next.js 15 SSR/SSG/ISR híbrido 1.5-2.5s Apps full-stack, e-commerce
Remix SSR puro 1.5-2s Apps con forms complejos
Astro SSG con Islands 0.5-1.5s Contenido estático, blogs
SvelteKit SSR/SSG híbrido 1-2s Apps de alto rendimiento
Nuxt 3 SSR/SSG/ISR 1.5-2.5s Ecosistema Vue

Astro y la Arquitectura de Islas

Astro tiene un enfoque radical: envía cero JavaScript por defecto. Solo los componentes que marques explícitamente como interactivos se hidratan en el cliente.

---
// Astro Component - Solo HTML por defecto
import Counter from '../components/Counter.jsx'
---
<div>
  <h1>Mi Blog</h1>
  <p>Este contenido es HTML puro, sin JS</p>
  <!-- Este componente sí tiene JS -->
  <Counter client:visible />
</div>

client:visible le dice a Astro: "Solo carga el JS de este componente cuando sea visible en viewport". El resto de la página es HTML estático.

Resultado: Sitios con 90+ en Lighthouse por defecto.

El problema del enrutamiento client-side

Incluso con SSR/SSG, si usas React Router en modo BrowserRouter, puedes tener problemas:

// Problema: Google puede no descubrir estas rutas
<BrowserRouter>
  <Routes>
    <Route path="/productos" element={<Products />} />
    <Route path="/servicios" element={<Services />} />
  </Routes>
</BrowserRouter>

Google descubre páginas siguiendo enlaces (<a href>). Si tus rutas solo existen en JavaScript y no hay <a> tags reales, Google no las encuentra.

Solución: Genera un sitemap.xml con todas tus rutas y usa `` components que generan <a> tags reales.

Structured Data: El SEO que no ves

No basta con que Google indexe tu contenido, necesitas structured data (JSON-LD) para aparecer en rich snippets.

// components/ProductSchema.tsx
export function ProductSchema({ product }: { product: Product }) {
  const schema = {
    "@context": "https://schema.org",
    "@type": "Product",
    "name": product.name,
    "image": product.image,
    "description": product.description,
    "offers": {
      "@type": "Offer",
      "price": product.price,
      "priceCurrency": "USD",
      "availability": "https://schema.org/InStock"
    }
  }
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  )
}

Esto le dice a Google: "Esto es un producto con precio, disponibilidad, etc." y puede mostrarlo en resultados enriquecidos.

Verificación en Google Search Console

Una vez que implementes SSR/SSG, verifica que Google puede ver tu contenido:

  1. Abre Google Search Console
  2. Ve a URL Inspection
  3. Pega una URL de tu sitio
  4. Haz clic en "Test Live URL"
  5. Revisa la pestaña "Rendered HTML"

Si ves tu contenido completo ahí, Google puede indexarlo. Si ves solo <div id="root"></div>, tienes un problema de renderizado.

Matriz de decisión: ¿Qué framework usar?

Tipo de Proyecto Renderizado Herramienta
Dashboard privado (login required) CSR (SPA normal) Vite + React Router
Blog / Portfolio SSG Astro, Next.js
E-commerce SSR o ISR Next.js, Remix
Landing page marketing SSG Astro
Docs / Knowledge base SSG Astro, Docusaurus
App con datos en tiempo real SSR + CSR híbrido Next.js, SvelteKit
Sitio corporativo SSG Astro

Hybrid Rendering: Lo mejor de ambos mundos

Para apps complejas, puedes mezclar estrategias en Next.js App Router:

// app/layout.tsx - Layout global
export default function RootLayout({ children }) {
  return <html lang="es">{children}</html>
}
// app/page.tsx - SSG (landing page)
export default function Home() {
  return <LandingPage />
}
// app/blog/[slug]/page.tsx - ISR (blog)
export const revalidate = 3600 // 1 hora
// app/dashboard/page.tsx - CSR (no SEO needed)
'use client'
export default function Dashboard() {
  const { data } = useSWR('/api/user-data')
  return <DashboardContent data={data} />
}

Cada ruta usa la estrategia óptima para su caso de uso.

El error fatal: Migrar sin pensar en SEO

He visto empresas migrar de WordPress a React SPA y perder el 70% del tráfico orgánico en 3 meses. El patrón:

  1. Contratan desarrollador frontend que no entiende SEO
  2. Usan create-react-app porque "es más fácil"
  3. Lanzan la nueva web sin verificar en Search Console
  4. 2 meses después: "¿Por qué no tenemos visitas?"

Proceso correcto:

  1. Audita el tráfico orgánico actual (Google Analytics)
  2. Identifica las páginas con más tráfico SEO
  3. Elige framework con SSR/SSG (Next.js, Remix, Astro)
  4. Implementa redirects 301 si cambias URLs
  5. Genera sitemap.xml con todas las rutas
  6. Verifica en Search Console antes de lanzar
  7. Monitorea tráfico post-lanzamiento semanalmente

Checklist de optimización SEO para SPAs

[ ] Framework con SSR/SSG elegido (Next.js, Remix, Astro)
[ ] Meta tags dinámicos implementados (react-helmet, next/head)
[ ] Sitemap.xml generado automáticamente
[ ] Robots.txt configurado correctamente
[ ] Structured Data (JSON-LD) en páginas clave
[ ] Core Web Vitals pasando (LCP < 2.5s, INP < 200ms, CLS < 0.1)
[ ] Bundle JS optimizado (code splitting, tree shaking)
[ ] Imágenes optimizadas (next/image, srcset, lazy loading)
[ ] Verificado en Google Search Console
[ ] Cache-busting en archivos JS (fingerprinting)

Conclusión

Si estás haciendo un panel de administración privado (dashboard que requiere login), una SPA pura con CSR está perfecta. El SEO no importa porque Google no va a indexar páginas detrás de autenticación.

Pero si estás haciendo un e-commerce, una landing page, un blog o cualquier sitio público donde el tráfico orgánico importa, y usas create-react-app o Angular CLI sin pre-renderizado, estás sacrificando hasta el 40% de tu potencial de indexación.

La buena noticia: las herramientas modernas (Next.js 15, Remix, Astro) hacen que SSR/SSG sea casi tan fácil como CSR. Solo necesitas elegir la herramienta correcta desde el principio.

SSR y SSG resuelven el 90% de los problemas de SEO en JavaScript. El 10% restante es optimización de rendimiento y contenido de calidad.

— David Morales Vega

Fuentes y recursos

Sigue leyendo

¿Quieres aplicarlo en tu empresa?

Cuéntanos qué necesitas: lo vemos juntos y te proponemos un camino por escrito, en español o en inglés.

[email protected]