SPA SEO(SPA)
SPA SEO is the practice of making Single-Page Applications (React, Vue, Angular, Svelte) discoverable and indexable. The rendering strategy is the primary decision: SSR, SSG, hybrid, or dynamic rendering. Pure client-side rendering forces Google's WRS into a second-wave fetch, slowing indexing.
Long definition
A Single-Page Application loads one HTML shell and uses JavaScript to render every subsequent view. The SEO problem is that Googlebot's first request returns HTML containing little more than <div id="root"></div> — the actual content depends on the JS bundle running in Google's Web Rendering Service (WRS, a headless Chromium).
Google handles this in two waves: first-pass indexing of the raw HTML (often empty for SPAs), then second-pass after WRS executes JS. The second wave runs hours to weeks after the first. For sites with many URLs, news, or any time-sensitive content, that delay is fatal.
The four main strategies:
- SSR (Server-Side Rendering) — every request is rendered to HTML on the server. Next.js, Nuxt, Remix, SvelteKit. First-pass indexing sees the full content. Best SEO; highest server cost.
- SSG (Static Site Generation) — pages are pre-rendered at build time into static HTML. Same SEO benefits as SSR, near-zero runtime cost. Limitation: not for personalized or rapidly-changing content. Astro, Next.js with
getStaticProps, Hugo, Gatsby. - Hybrid — SSG for stable pages (marketing, blog), SSR for personalized (account, cart), CSR for highly interactive (dashboards). Modern frameworks default to this.
- Dynamic rendering — serve pre-rendered HTML to bots, normal CSR to users, distinguished by user-agent. Google has deprecated it as a long-term solution since 2022, calling it a "workaround". Acceptable transitional measure; not where you want to land.
Pure CSR is appropriate only for behind-login dashboards, internal tools, and apps that don't need search traffic. Anything user-facing with SEO ambitions needs at least SSG for the indexable surface.
Beyond rendering, the SPA-specific traps: client-side routing must update the URL via the History API (not hash fragments — /#/about is one URL to Google); meta tags and canonical must be set per route, not just on the shell; soft 404s are common (deleted route returns 200 with "not found" text — fix by returning a real 404 from the server).
Common misconceptions
- "Google indexes SPAs fine since 2019." Google can render JS, but the second-wave indexing delay still applies, and the rendering budget is finite. A complex SPA with slow hydration may run out of execution time before content appears. Server rendering is still the safer default.
- "Hash routing (
/#/page) works for SEO." It doesn't. Everything after#is a fragment identifier; Google treats/pageand/#/pageas the same URL. Use the History API to produce real paths. - "SSR fixes everything." SSR fixes the rendering wave problem. It doesn't fix slow APIs, missing meta tags per route, or bad internal linking. The render is the prerequisite, not the whole job.
- "
react-helmetupdates are enough." They are — provided rendering produces the meta tags in the initial HTML response. Client-only Helmet updates are processed in the second wave and inconsistently picked up.
Continue exploring