24 July 2026

SEO for Single Page Applications (SPAs): How to Get React and Vue Sites Indexed

Anjan Luthra
Anjan Luthra

Managing Partner · 8 min read

Key Takeaways

  • A traditional website sends a fully formed HTML document to the browser for every URL a user visits.
  • Most technical discussions about SPA optimisation eventually converge on the same three options.
  • Rendering strategy tends to absorb all of the attention in SPA SEO discussions.
  • On a standard HTML site, the <title> tag, meta description, canonical tag, and Open Graph data are written directly into the document <head> .
  • The instinct is often to run a standard crawl tool across the site and review the results.
  • Googlebot can execute JavaScript and render React or Vue applications, but the process is deferred and subject to queue delays.
  • The core problem with SPA SEO is that it is invisible until you look for it deliberately.

React and Vue applications often look flawless in a browser and perform terribly in search. The reason is rarely bad content or weak links — it is almost always an indexing problem that was baked into the architecture before a single page of copy was written. Engineers optimise for the user experience they can see; Googlebot experiences something quite different. This guide explains the specific mechanics behind that gap, and what your development team needs to do about it.

If you're looking for expert help in this area, explore how Indexed's technical SEO services can drive measurable results for your business.

What Makes SEO for Single Page Applications Different

A traditional website sends a fully formed HTML document to the browser for every URL a user visits. A search engine crawler receives that same HTML, reads the content, and indexes it. The process is straightforward because the content exists in the document at the moment it arrives.

A single page application works differently. The server delivers a minimal HTML shell — often little more than a <div id="root"></div> and a bundle of JavaScript. The browser then executes that JavaScript to fetch data and render the actual content. From a user perspective, the experience feels fast and seamless. From a crawler's perspective, it sees an empty page unless it waits for the JavaScript to execute.

SEO for single page applications is therefore not primarily a content problem or a keyword problem. It is a rendering problem. Until a crawler can reliably read the content your JavaScript generates, no amount of on-page optimisation will fully compensate.

How Googlebot Handles JavaScript

Google has improved its ability to render JavaScript considerably over the past several years. Googlebot does execute JavaScript during what Google calls a second wave of indexing. However, this deferred rendering introduces delays — sometimes days between crawl and index — and consumes significantly more server and crawl budget resources than static HTML. Google's own documentation is candid that JavaScript-dependent content may be indexed later than HTML-based content, and may not be indexed at all if the rendering queue is congested.

Other search engines — Bing, for instance — have less mature JavaScript rendering pipelines, meaning a purely client-side SPA is likely to perform worse in non-Google search than the same content served as HTML.

The Three Rendering Strategies and When to Use Each

Most technical discussions about SPA optimisation eventually converge on the same three options. What they often skip is a frank assessment of which option is appropriate for which type of business. Here is that assessment.

Server-Side Rendering (SSR)

With server-side rendering, every request triggers the application to render the full HTML on the server before sending it to the browser. Frameworks like Next.js (React) and Nuxt.js (Vue) make SSR achievable without rebuilding the application from scratch. The crawler receives fully populated HTML on first request — the same experience as a traditional site.

SSR is the strongest option for content-heavy sites that depend on organic search: editorial publications, e-commerce catalogues, SaaS marketing sites. The trade-off is infrastructure cost and complexity. Server rendering at scale requires more compute and careful caching strategy. For teams already running Next.js or Nuxt.js, SSR is usually the right default position.

Static Site Generation (SSG)

Static site generation pre-renders every page at build time and deploys the output as static HTML files. Tools like Next.js, Gatsby (React) and Nuxt generate static output that CDNs can serve instantly. Because the HTML already exists when a crawler arrives, there is no rendering delay, no queue dependency, and no JavaScript execution required.

SSG is the strongest option for sites where content does not change per-user or per-request: blogs, documentation, landing pages, brochure sites. It is genuinely excellent for SEO. Its limitation is that dynamic, user-specific content — personalised dashboards, real-time feeds — cannot be statically generated.

Dynamic Rendering

Dynamic rendering serves pre-rendered HTML specifically to crawlers while serving the normal JavaScript SPA to human users. A reverse proxy (or middleware) detects the user-agent, routes crawlers to a pre-rendered snapshot, and routes browsers to the live application. Tools like Rendertron or commercial services can automate this process.

Google acknowledges dynamic rendering as a workaround rather than a permanent solution, and describes it as an interim measure while SSR adoption matures. It introduces maintenance overhead — you are now serving two different responses for the same URL, which must be kept in sync — and it carries a small cloaking risk if implemented carelessly. Use it when a full SSR migration is not feasible in the near term, not as a substitute for it.

Free · No obligation

Find out what your site is losing in organic revenue.

In a free Revenue Gap Analysis, we show you exactly what's holding your rankings back — and what fixing it is worth in real revenue.

See my revenue opportunity →

URL Structure and Crawl Budget: The Problems Most Teams Miss

Rendering strategy tends to absorb all of the attention in SPA SEO discussions. Two equally important issues receive far less coverage: URL structure and crawl budget efficiency.

Hash-Based URLs

Early JavaScript frameworks defaulted to hash-based routing, where navigation changes only the fragment identifier in the URL — for example, example.com/#/products rather than example.com/products. From a search engine perspective, everything before the hash is the URL. Everything after it is invisible to the crawler. This means every route in a hash-based SPA is, from Google's viewpoint, the same page as the homepage.

If your application is using hash routing, the fix is to migrate to the HTML5 History API, which enables real path-based URLs. React Router, Vue Router, and Angular's Router all support this natively. It requires server configuration to return the SPA shell for any valid route rather than a 404, but this is a necessary trade-off for indexability.

Crawl Budget in JavaScript-Heavy Sites

Google allocates a crawl budget to each site based on crawl demand and crawl rate limits. JavaScript rendering consumes significantly more resources per URL than fetching a static HTML page. Sites with large numbers of routes — e-commerce with hundreds of product categories, SaaS platforms with deep navigation — can find that their crawl budget is exhausted before Googlebot has processed a meaningful proportion of their content.

The practical consequence is that new or updated content may take far longer to appear in search than it would on a comparable HTML site. Monitoring crawl stats in Google Search Console — specifically the Crawl Stats report — will show you average response times and whether Googlebot is encountering errors during rendering. Elevated response times for JavaScript resources are a signal that rendering is creating bottlenecks.

Meta Tags, Structured Data, and the Hydration Timing Problem

On a standard HTML site, the <title> tag, meta description, canonical tag, and Open Graph data are written directly into the document <head>. On a client-side SPA, these are typically injected by JavaScript after the page loads. This creates a timing problem: if a crawler does not wait for JavaScript execution, it indexes the shell's default title — often something like the application name or nothing at all — rather than the page-specific metadata.

Managing Meta Tags in React and Vue

In React applications, libraries such as React Helmet Async allow metadata to be declared per-component, but this only helps if the rendering layer actually executes that JavaScript. When paired with SSR in Next.js, the <Head> component writes metadata into the server-rendered HTML document before it reaches the crawler — the correct approach. In Vue applications, Nuxt.js provides a useHead composable that achieves the same outcome under SSR.

If your application is purely client-side rendered, metadata injection via JavaScript is unreliable for SEO. The correct long-term resolution is a move to SSR or SSG, not a more sophisticated client-side metadata library.

Structured Data in SPAs

JSON-LD structured data — for products, articles, FAQs, breadcrumbs — can be rendered client-side in SPAs, but the same timing risk applies. The safest implementation is to include JSON-LD in the server-rendered HTML response. Under Next.js, this means populating structured data in getServerSideProps or getStaticProps and writing it into the document head rather than appending it via a client-side effect.

Auditing Your SPA for Technical SEO Issues

The instinct is often to run a standard crawl tool across the site and review the results. The problem is that most crawl tools, by default, do not render JavaScript — so they report the empty shell, not the rendered content. The audit then produces a long list of missing titles and empty body tags that reflects the tool's limitations rather than the site's actual content.

A technically accurate SPA audit requires:

  • URL inspection via Search Console — Use the URL Inspection tool on representative pages. The "More info" tab under Coverage shows the last crawl date, the rendered HTML Google actually indexed, and any JavaScript errors encountered during rendering. This is the most reliable view of what Google sees.
  • Screaming Frog with JavaScript rendering enabled — Configure Screaming Frog to render JavaScript using its built-in Chromium renderer. Compare the crawl results with a no-JavaScript crawl to identify what content depends entirely on client-side execution.
  • Rich Results Test and Mobile-Friendly Test — Both tools in Google Search Console render JavaScript and show you the page as Googlebot experiences it. They are useful for confirming whether structured data and metadata are present after rendering.
  • Server response header inspection — Confirm that your SPA returns a meaningful HTTP status code per route, not a blanket 200 with an error message rendered in JavaScript. A 404 page that returns a 200 status (a soft 404) will be indexed as valid content.

See the system

The Full-Stack Search Method.

Seven compounding pillars that turn search into your highest ROI channel. See exactly how we build organic growth that lasts.

See the full methodology →

FAQ

Can Google index a React site without server-side rendering?

Yes, but with caveats. Googlebot can execute JavaScript and render React or Vue applications, but the process is deferred and subject to queue delays. Pages may take longer to appear in search than equivalent HTML content, and the indexing quality depends on whether the rendering completes successfully. For sites where organic search is a meaningful acquisition channel, relying solely on client-side rendering carries meaningful indexing risk.

Does hash-based routing really prevent indexing?

In practice, yes. Google treats the fragment identifier — the portion of the URL after the hash — as a client-side instruction that does not form part of the page URL. This means all hash-routed pages are treated as the same URL as the base path. Migrating to path-based routing via the HTML5 History API is a prerequisite for meaningful SPA indexing.

Which is better for SEO — SSR or SSG?

Both deliver pre-rendered HTML to crawlers and are significantly better than client-side rendering alone. SSG has a slight edge in raw crawl efficiency because pages are served as static files with minimal server overhead. SSR is more appropriate where content must be personalised or fetched at request time. The distinction matters less than the underlying principle: get full HTML to the crawler without requiring JavaScript execution.

Is dynamic rendering considered cloaking by Google?

Google has explicitly stated that correctly implemented dynamic rendering — where crawlers receive a pre-rendered version of the same content served to users — is not considered cloaking. The risk arises if the pre-rendered version contains materially different content from what users see. Provided the rendered snapshot accurately reflects the live application, dynamic rendering is permissible. That said, Google continues to describe it as an interim workaround rather than a recommended long-term architecture.

What to Do This Week

If you manage or oversee a SPA-based site and organic search is part of your acquisition strategy, take these specific steps before anything else:

  • Run the URL Inspection tool on your five highest-value pages in Google Search Console. Look at the rendered HTML screenshot. If the content is missing or incomplete, you have a confirmed rendering problem — not a suspected one.
  • Check your URL structure in your browser's address bar as you navigate. If the URL contains a hash before the route (e.g. /#/about), raise this with your development team immediately. It is a fundamental indexing blocker.
  • Ask your development team which framework version you are running and whether SSR or SSG is configured. Next.js and Nuxt.js both support these modes — the question is whether your team has enabled them.
  • Pull the Crawl Stats report from Search Console (Settings > Crawl Stats). If average response time is elevated or if you see a high proportion of failed JavaScript resources, your rendering pipeline is creating crawl budget pressure.
  • Prioritise SSR or SSG for your most commercially important pages rather than attempting a site-wide migration immediately. Product category pages, landing pages, and high-intent content should be the first candidates.

The core problem with SPA SEO is that it is invisible until you look for it deliberately. An application that works perfectly for users can be almost entirely absent from search — and the team responsible for it may have no idea. The audit steps above will tell you within a day whether you have a problem worth escalating.

Anjan Luthra

Written by

Anjan Luthra

Managing Partner, Indexed

Anjan Luthra is Managing Partner at Indexed. He has spent over a decade inside high-growth companies building organic search into their primary acquisition channel, and writes about SEO strategy, AI search, and revenue a…

Share

Get SEO insights that actually move the needle.

Strategy, AI search, and growth tactics from the Indexed team — straight to your inbox.

Unsubscribe anytime. No spam.