Preferences

Privacy is important to us, so you have the option of disabling certain types of storage that may not be necessary for the basic functioning of the website. Blocking categories may impact your experience on the website. More information (Cookie Policy)

Accept all cookies

Single Page Application SEO: How to Get a SPA Indexed by Google and Read by AI

Single page application SEO: why SPAs fail to rank, how SSR, static generation and prerendering fix it, and how to make your app readable by AI crawlers.

Published on:
19/10/2026
-
Updated on:
19/10/2026
Cover of the Sorank guide to single page application SEO, comparing client-side and server-side rendering

Tools

Thibault Besson-Magdelain, founder of Sorank

About Author

Thibault Besson-Magdelain

Founder of Sorank, 7+ years of experience in SEO, GEO enthusiast.
On this page

In short

a single page application (SPA) loads one HTML shell and builds every view with JavaScript in the browser. Google can render JavaScript, but rendering is delayed and fragile, and the crawlers behind ChatGPT, Claude and Perplexity do not execute it at all. To rank and get cited, each view of a SPA needs its own URL, a real link with an href, a unique title and description, a correct status code and, above all, its main content present in the HTML the server sends, through server-side rendering, static generation or prerendering.

Want to go further? Ask

Single page application SEO is the set of techniques that make a JavaScript app built with React, Vue, Angular or Svelte as easy to crawl as a classic website. The problem is well documented: Google explains in its JavaScript SEO basics that it processes JavaScript pages in three phases (crawling, rendering, indexing), and every SPA weakness sits somewhere in that pipeline. This guide focuses on SPAs; for JavaScript sites in general, read our guide to JavaScript SEO.

What is a single page application, and why is it hard for SEO?

A traditional website returns a complete HTML document for every URL. A SPA returns almost the same document for every URL: an empty <div id="root">, a few meta tags and a large JavaScript bundle. The browser downloads the bundle, calls an API, then draws the page. When the visitor clicks a link, JavaScript swaps the content without reloading.

For a human with a fast phone, the result feels instant. For a crawler, the first response is an empty shell: no heading, no text, no links. Everything depends on whether the bot executes the JavaScript, and when. That single fact explains most SPA SEO problems:

  • Delayed or missing content: the text exists only after rendering, which Google performs later and other bots skip.
  • One URL for many views: older SPAs route with fragments (/#/pricing), and the part after the # is never sent to the server, so all views collapse into one URL.
  • Identical titles and descriptions: the shell carries one <title>, and if the router does not update it, every page shares it.
  • Links without href: buttons and onClick handlers navigate for users but give crawlers nothing to follow.
  • Soft 404 errors: the server answers 200 for every path, including pages that do not exist, so error views get indexed as thin pages.
  • Heavy JavaScript: large bundles slow down the first display and interactions, which hurts the Core Web Vitals.

How does Google crawl and render a SPA?

Googlebot first fetches the raw HTML and reads the links and directives it finds there. Pages that return a 200 status are then put in a queue for the Web Rendering Service, a headless Chromium. Google states that a page "may stay on this queue for a few seconds, but it can take longer than that". Once rendered, the resulting HTML is parsed again for content and links, then indexed.

Four details from Google's documentation matter for SPAs:

  1. Only <a> elements with an href are reliable links. Links injected by JavaScript are fine as long as they follow that format.
  2. Fragments are not separate pages. Google asks you to use the History API (pushState) instead of # routes. It deprecated its old AJAX crawling scheme (the #! URLs) in October 2015.
  3. A noindex in the original HTML wins. Google may skip rendering when it sees noindex, so removing it with JavaScript later does not work.
  4. Rendering uses cached files. Googlebot caches JavaScript and CSS aggressively, so use fingerprinted file names (a content hash in the file name) to avoid outdated bundles.

The practical consequence: Google can index a client-rendered SPA, but you add a second step, a delay and a failure point to every page. A timeout, a blocked API call or a JavaScript error during rendering leaves the indexed version empty. Check what Google sees with the URL Inspection tool in Search Console, and read our guide on how Google indexing works if pages stay out of the index.

Do ChatGPT, Claude and Perplexity read JavaScript?

Mostly not. In a study published in December 2024 on the traffic of its network, Vercel and MERJ found that "none of the major AI crawlers currently render JavaScript": GPTBot, OAI-SearchBot and ChatGPT-User (OpenAI), ClaudeBot (Anthropic), PerplexityBot, Meta-ExternalAgent and Bytespider all read the raw HTML only. They do download JavaScript files (about 11.5% of ChatGPT's requests and 23.8% of Claude's) but do not run them. The exceptions are Gemini, which relies on Google's rendering infrastructure, and AppleBot, which uses a browser-based crawler.

For a client-rendered SPA, this means the AI crawlers that feed ChatGPT, Claude and Perplexity see an empty page. Your product can rank on Google and still be invisible in AI answers, because the assistant never read a word of it. The same applies to most link preview bots on social networks and messaging apps, which read the Open Graph tags in the raw HTML. Once the content is in the HTML, also check that your robots.txt allows the AI crawlers you want.

Which rendering strategy should a SPA use?

The fix is to send real HTML on the first response. Google calls dynamic rendering (serving a prerendered version to bots only) "a workaround and not a recommended solution" in its dynamic rendering documentation, and points to server-side rendering, static rendering or hydration instead. The definitions below follow the Rendering on the Web reference on web.dev.

StrategyHow it worksContent in the first HTMLBest forLimits
Client-side rendering (CSR)The browser builds the page from JavaScript and an APINoDashboards and pages behind a loginDelayed indexing on Google, invisible to most AI crawlers
Server-side rendering (SSR)The server renders HTML on each request, then JavaScript hydrates itYesCatalogs, listings, content that changes often or is personalizedServer cost, slower response time if not cached
Static generation (SSG)HTML is generated at build time for each URL and served from a CDNYesMarketing pages, blog, documentation, landing pagesRebuild needed when content changes (incremental options exist)
Prerendering at build timeA headless browser captures the initial state of each route as static HTMLYesExisting SPAs with a limited number of public routesData frozen at build time, routes must be listed
Dynamic renderingBots receive a prerendered snapshot, users the SPAYes, for listed bots onlyTemporary fix for a legacy appOfficially a workaround, maintenance cost, risk of serving bots different content

Most modern frameworks let you mix strategies by route: static for marketing pages, server-rendered for the catalog, client-rendered for the logged-in app. That hybrid setup gives crawlers full HTML where search traffic happens and keeps the SPA experience where it adds value.

Framework options

LibraryFramework or feature for HTML on first load
ReactNext.js (server components, SSR and static generation), React Router in framework mode, Gatsby
VueNuxt (universal rendering by default, static generation available)
AngularAngular SSR (server rendering and build-time prerendering)
SvelteSvelteKit (server rendering by default, per-route prerendering)
Any of themAstro (static HTML by default, interactive islands loaded only where needed)

Sites generated by AI website builders deserve a special mention: many export a client-rendered React app. If you built your site with one, see our notes on Lovable SEO and GEO before assuming Google and ChatGPT can read it.

SPA SEO checklist: what to fix route by route

1. Give every view a clean, crawlable URL

Use the History API (/pricing, never /#/pricing), one URL per piece of content, and the same URL whether the visitor arrives directly or through in-app navigation. Every URL must load the right content when opened in a new tab, which is the test a crawler performs.

Every navigation element that leads to an indexable page should be an <a href="/path">. Framework link components (Link, NuxtLink, routerLink) output this markup; custom click handlers on div or button elements do not. Good links also carry your site architecture: if a page is not linked, crawlers rarely find it.

3. Update title, description and canonical per route

Each route needs its own title, meta description, canonical and Open Graph tags, generated on the server when possible. Our page on meta tags lists what to set. Keep a single canonical tag: Google recommends setting it in the HTML, and if JavaScript changes it, the result must not contradict the original.

4. Return honest status codes

Missing products and deleted articles must not answer 200 with an error message. With SSR, return a real 404 or 410 from the server. In a pure SPA, Google accepts two fallbacks: redirect with JavaScript to a URL that returns a 404, or add a noindex robots meta tag to the error view.

5. Put the main content and structured data in the HTML

Headings, body text, prices, FAQ and JSON-LD structured data should be in the server response, not loaded after a click or a scroll. Content behind tabs that are rendered on demand, or behind "load more" buttons, is often missed.

6. Keep the JavaScript light

Split the bundle by route, defer what is not needed for the first view and avoid re-rendering the whole page during hydration. Note that Chrome field data (CrUX) attributes the metrics of in-app navigations to the URL the visitor first loaded, so test your key templates individually.

7. Test what bots receive

Compare three versions of each key template: the raw HTML (view the source or fetch the page with a command-line tool), Google's rendered HTML (URL Inspection, then "View crawled page") and the page with JavaScript disabled. If the main text only appears in the second version, AI crawlers do not see it. Our free crawlability audit for AI and the indexability checker run these checks on any URL.

How do you know if your SPA has an SEO problem?

SymptomLikely causeFix
Search Console shows many pages "Crawled, currently not indexed"Empty or near-identical HTML before renderingSSR, SSG or prerendering for public routes
Every result in a site: search shows the same titleTitle not updated per routeRoute-level head management on the server
Only the home page is indexedHash routing or links without hrefHistory API and real <a> links
Error pages appear in the indexSoft 404s (status 200 everywhere)Server 404, or JavaScript redirect to a 404 URL, or noindex
Ranks on Google but never cited by ChatGPT or PerplexityContent only visible after JavaScript executionContent in the first HTML response
Old content shown in Google's rendered viewCached JavaScript or CSS filesFingerprinted file names

SPA SEO and GEO: being found on Google and cited by AI

For years, client-side rendering was a manageable risk because Google rendered JavaScript eventually. With part of the discovery now happening in ChatGPT, Claude, Gemini and Perplexity, the cost has changed: a page that exists only after JavaScript execution is absent from the sources most assistants can read. Crawlability in the raw HTML has become the first condition of generative engine optimization.

Once the HTML is complete, the rest follows the usual rules: pages that answer one question clearly, with sourced facts and structured data, are the ones assistants quote. Sorank's GEO optimization features help you write and publish that content, and AI mention tracking shows whether ChatGPT, Claude and Gemini cite your brand after the rendering fix.

The key rule of single page application SEO fits in one sentence: whatever you want Google to rank and AI to cite must be in the HTML the server sends. Keep the SPA for the logged-in experience, render public pages on the server or at build time, then test what bots receive before and after each release.

Frequently asked questions

Is a single page application bad for SEO?

A single page application is not penalized by Google, but its default setup, client-side rendering with one HTML shell, makes indexing slower and less reliable. Most AI crawlers do not execute JavaScript, so they see an empty page. With server-side rendering, static generation or prerendering on public routes, a SPA can rank as well as any classic website.

Is React good for SEO?

React itself is neutral: what matters is where the HTML is produced. A React app rendered only in the browser has the usual SPA problems, while the same components rendered on the server with Next.js or React Router in framework mode deliver complete HTML to crawlers. Choose a framework that supports server or static rendering for the pages you want indexed.

Should I use dynamic rendering for my SPA?

Only as a temporary measure. Google describes dynamic rendering as a workaround and not a recommended solution, because maintaining a separate version for bots adds cost and risk. Server-side rendering, static generation or build-time prerendering give the same HTML to every visitor and are the long-term fixes.

Our Blog for Ambitious Company

Our free SEO tools

See all our free tools

THEY TRIED SORANK BEFORE YOU

Testimonials from ambitious founders

Organic growthE-commerce

0 to 10000 visitors in 5 months

Celio Fabianomonrobotlavevitre.fr
Organic growthSoftware

0 to 5000 clients in 8 months

Thibault BMsorank.com
Organic growthLeisure and aviation

0 to 1104 clicks in 4 months

Dominiqueflight-sensations-idf.com
Organic growthMusic software

x30 clicks in 4 months

Valentin Cordiertiger-sounds.com
Organic growthHealth and wellness

x30 clicks in 4 months

Rodrigue Bretonalthevia.com