← All articles
spa seo

SPA SEO: Solving Search Engine Crawling for React, Vue, and Angular Apps

Muginai Team · · 4 min read · 845 words

The fundamental SPA SEO challenge: Google can execute JavaScript, but rendering pipelines, crawl budget allocation, and indexation timing differ from HTML-rendered pages. A SPA that renders all content client-side may appear as a blank page in Google’s crawl queue, then render correctly in a second crawl after Googlebot runs its JavaScript queue — introducing delays of days to weeks between publication and indexation.

How Google Crawls JavaScript

Google’s crawl process for JavaScript sites involves two stages:

Stage 1 (HTML crawl): Googlebot fetches the URL and receives the initial HTML response. For a SPA, this is often just a minimal HTML shell: <div id="root"></div> with a JavaScript bundle reference.

Stage 2 (JavaScript rendering): Googlebot queues the page for JavaScript rendering. Rendering resources are finite — Google maintains a “rendering queue” and processes pages when capacity allows. This queue introduces indexation delay of hours to weeks depending on site crawl priority.

The practical implication: content that appears only after JavaScript execution may not be indexed, or may be indexed days after publication, or may have inconsistencies between the HTML-rendered and JS-rendered versions.

Server-Side Rendering (SSR)

SSR renders the full page HTML on the server before sending it to the browser. The initial HTML response contains the complete content — no JavaScript rendering required for indexation.

SSR implementation: Next.js (React), Nuxt.js (Vue), and Angular Universal provide SSR frameworks. Each page request triggers server-side rendering and delivers complete HTML.

SSR SEO advantages:

  • No indexation delay — Google gets complete content in stage 1 crawl
  • Consistent between Googlebot and users — no two-stage rendering discrepancy
  • Core Web Vitals improvement — LCP improves significantly when content is in initial HTML

SSR trade-offs:

  • Server load: each page request requires server rendering work
  • Time to first byte: server rendering adds latency compared to serving static files
  • Complexity: SSR adds infrastructure complexity vs. pure client-side rendering

Hydration: SSR typically “hydrates” on the client side — after the server-rendered HTML loads, the JavaScript framework takes over and makes the page interactive. Excessive hydration JavaScript can hurt INP even when LCP is good.

Static Site Generation (SSG)

SSG pre-renders all pages at build time and serves static HTML files. The gold standard for SEO performance.

SSG implementation: Next.js getStaticProps, Gatsby, Astro (component-level SSG). Build process generates HTML for every URL in advance.

SSG SEO advantages:

  • Best possible crawl experience — pure HTML, instant TTFB
  • Best Core Web Vitals — no server rendering latency, no hydration delay
  • CDN-cacheable at edge — fastest possible delivery globally

SSG limitations:

  • Build time: sites with millions of pages require long build processes
  • Real-time data: content that changes frequently (live prices, inventory) can’t be fully static
  • On-demand generation: Next.js Incremental Static Regeneration (ISR) addresses this — pages can be revalidated without full rebuild

Best for: Marketing sites, documentation, blogs, content sites with infrequent updates.

Dynamic Rendering

Dynamic rendering serves two different responses: server-rendered HTML to crawlers (Googlebot, Bingbot) and client-side JavaScript to browsers. Implemented via middleware that detects the user agent.

Implementation: Rendertron, Prerender.io, or custom middleware that detects bot user agents and serves pre-rendered HTML.

When to use: Legacy SPAs where migrating to SSR/SSG isn’t feasible in the short term. Dynamic rendering is a workaround, not a permanent solution — Google has stated they recommend SSR/SSG over dynamic rendering.

Risk: User agent detection inconsistencies can cause crawlers to receive the wrong version. Caching staleness can cause crawlers to see outdated content.

Diagnosing SPA SEO Issues

Google Search Console URL Inspection: Enter any SPA URL in the URL Inspection tool and compare “HTML” (stage 1) vs. “Screenshot” (after JS rendering). If the HTML version shows an empty shell and the screenshot shows content, the page depends on JavaScript rendering.

Cache:URL operator: cache:example.com/page shows Google’s cached version. If the cache shows the content, Google has successfully crawled it.

Google’s Mobile-Friendly Test / Rich Results Test: These tools render the page with JavaScript and show what Google sees after rendering. If the test shows empty content for a page that should have content, the JavaScript isn’t rendering correctly for crawlers.

Common SPA SEO Mistakes

React Router without SSR: Creating a SPA with React Router but without server-side rendering produces pages that Google may render as blank for multiple crawl cycles, particularly for deep or less-crawled pages.

Rendering-dependent internal links: If navigation links are only created after JavaScript renders, Google may not discover linked pages via crawl. Internal links should be present in the initial HTML response.

Title tags and meta tags set via JavaScript only: document.title = "Page Title" set by JavaScript works for users but may not be picked up in stage 1 crawls. Critical meta tags (title, description, canonical, robots) should be in the initial HTML, not set exclusively via JavaScript.

Hash-based routing: URLs with fragment identifiers (example.com/#/page) are not treated as separate pages by Google — the hash is not sent to the server, and all hash-based routes appear as the same URL to crawlers. Use path-based routing (example.com/page) for crawlable separate pages.

Stop doing SEO manually.

Muginai runs keyword research, content briefs, rank tracking, and backlink monitoring — autonomously, 24/7.

Get early access → All features Pricing
← Back to blog Explore features →