Saltar al contenido principal

SEO en React y Angular: Por Qué el 40% de SPAs No Aparece en Google

Problema de SEO en React y Angular: página en blanco en Google y soluciones con SSR y Next.js

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étricaDatoImpacto
Webs dependientes de JavaScript67%Mayor necesidad de optimización
SPAs con baja indexación40%Pérdida masiva de tráfico orgánico
Tráfico mobile-first indexado78%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:

text
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:

html
<!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):

RecursoImpacto en Crawl Budget
HTML estáticoMínimo (1x)
Página con JavaScriptAlto (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:

text
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étricaQué mideObjetivo
LCP (Largest Contentful Paint)Velocidad de cargamenos de 2.5 segundos
INP (Interaction to Next Paint)Responsividad (reemplazó FID en 2024)menos de 200ms
CLS (Cumulative Layout Shift)Estabilidad visualmenos de 0.1

SPA vs SSR en Core Web Vitals

FrameworkLCP típicoINP típicoProblema principal
React SPA (CSR)4-8s300ms+Bundle JS pesado
Angular CLI3-6s250ms+Hydration lenta
Next.js (SSR)1.5-2.5s100-150msMínimo
Astro (SSG)0.5-1.5s50-100msCasi 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.

tsx
// 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.

tsx
// 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.

tsx
// 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)

FrameworkRenderizadoLCP típicoMejor para
Next.js 15SSR/SSG/ISR híbrido1.5-2.5sApps full-stack, e-commerce
RemixSSR puro1.5-2sApps con forms complejos
AstroSSG con Islands0.5-1.5sContenido estático, blogs
SvelteKitSSR/SSG híbrido1-2sApps de alto rendimiento
Nuxt 3SSR/SSG/ISR1.5-2.5sEcosistema 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
---
// 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:

tsx
// 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 <Link> 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.

tsx
// 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 ProyectoRenderizadoHerramienta
Dashboard privado (login required)CSR (SPA normal)Vite + React Router
Blog / PortfolioSSGAstro, Next.js
E-commerceSSR o ISRNext.js, Remix
Landing page marketingSSGAstro
Docs / Knowledge baseSSGAstro, Docusaurus
App con datos en tiempo realSSR + CSR híbridoNext.js, SvelteKit
Sitio corporativoSSGAstro

Hybrid Rendering: Lo mejor de ambos mundos

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

tsx
// 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

text
[ ] 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

IconHablemos de tu proyecto!