Single-page applications made the web feel like software — instant navigation, no full reloads, app-like interactions. They also quietly broke one of SEO’s oldest assumptions: that the HTML a server sends is the content a crawler reads. In a default client-rendered SPA, the server sends an almost empty shell and JavaScript builds the page in the browser, so a crawler that does not execute that JavaScript sees nothing to index. The fix is not to abandon the SPA architecture; it is to send real HTML with server-side rendering. This guide explains why SPAs are risky for SEO, the rendering strategies that solve it, and where this bites on Shopify — which, for most merchants, is only when they go headless.
Why single-page applications are risky for SEO
A traditional website sends a fully-formed HTML document for every URL. A single-page application inverts that: the first response is a near-empty shell plus a JavaScript bundle, and the browser assembles the visible page by running that JavaScript and fetching data. Great for users on a fast device. A problem for anything that reads HTML without running scripts.
Crawlers are exactly that. To index a client-rendered SPA, a crawler has to download the shell, execute your JavaScript, wait for data to load, and only then see the content. Google can do this, but in a deferred second pass that can lag by days, and it breaks silently when a script errors or a resource is blocked. Bing is weaker at it. And the AI crawlers behind the answer engines are weaker still — many fetch raw HTML and never render at all. So the default SPA gambles your indexing on every crawler’s willingness and ability to run your code correctly, and that bet gets worse the more search shifts to AI.
The symptoms are familiar: pages that will not rank, a nearly-empty cached version in Google, social and AI previews that show a blank title, and a site that looks perfect to you and invisible to a bot. The root cause is always the same — the content is not in the HTML.
The fix: send real HTML
Every solution to SPA SEO does the same thing: make sure a crawler receives fully-rendered HTML for each URL without having to execute JavaScript. There are three ways to get there.
Server-side rendering (SSR) builds the HTML on the server for each request, then hydrates it into a live app in the browser. The crawler gets complete content on the first response; the user still gets the SPA experience after hydration. This is the most robust option and the right default for content that changes — product pages, search results, anything personalized or frequently updated. Frameworks like Next.js, Nuxt, Remix, and Shopify’s Hydrogen are built around it.
Static site generation (SSG) / prerendering builds the HTML ahead of time at deploy, serving a static file per route. It is the fastest and most crawlable option, ideal for pages that are the same for everyone — marketing pages, blog posts, documentation. The limit is that content is fixed until the next build, so it suits stable pages, not a live catalog.
Dynamic rendering detects crawlers and serves them a separately-prerendered HTML version while users get the normal SPA. Google now treats this as a legacy workaround rather than a recommendation, and it adds a whole rendering pipeline to maintain, but it can be a pragmatic bridge for an existing client-only app you cannot rebuild yet.
For most teams starting fresh, SSR (with static generation for the pages that qualify) is the answer. It removes the dependency on a crawler’s renderer entirely, which is the only way to make indexing deterministic across Google, Bing, and the AI engines at once.
SPA SEO fundamentals beyond rendering
Server-rendering the HTML is necessary but not sufficient. A SPA also has to respect the things a multi-page site gets for free.
Real URLs per view. Every indexable state needs its own crawlable URL using the History API, not a fragment (#) or an in-memory state. If a view has no URL, it cannot be indexed or linked. Per-route metadata. Each route must set its own title, meta description, and canonical — updated as the user navigates, and present in the server-rendered HTML, not only patched in by client JavaScript after load. Structured data per page. Emit the JSON-LD — Product, Article, FAQPage — in the server response so it is there for the first read. Correct status codes. A server-rendered app can return a real 404 or 301; a client-only app often returns 200 for everything, which pollutes the index with soft-404s. Clean internal links. Use real anchor tags with href attributes so crawlers can follow them, not click handlers on divs.
Get the rendering right and neglect these and you are back to invisible pages — for a different reason.
Where this actually bites on Shopify
Most Shopify merchants never touch any of this, and it is worth being clear about why. A standard Shopify storefront on a Liquid theme is server-rendered: Shopify sends complete HTML for every product, collection, and page. Your SEO work there is content, meta, schema, and indexing — the classic Shopify SEO stack — not rendering, because the platform already renders on the server.
SPA SEO becomes your problem only when you go headless: building a custom storefront with Shopify’s Hydrogen framework or a hand-rolled React, Vue, or Svelte front end talking to the Storefront API. That decoupling buys design and performance freedom and hands you back responsibility for rendering. This is exactly why Hydrogen server-renders by default and deploys on Oxygen — Shopify learned that a client-only headless storefront is an SEO liability, so its framework closes the gap for you. A custom headless build that renders only in the browser is where merchants recreate every problem in this guide.
The practical rule: if you are on a Liquid theme, this is not your concern — optimize content and schema. If you are going headless, choose an SSR framework from day one, and treat client-only rendering as a mistake to avoid rather than a phase to fix later.
SSR and AI search
Server-side rendering matters more in the AI era, not less. The crawlers behind ChatGPT, Perplexity, Claude, and the AI Overviews are generally worse at executing JavaScript than Google is; many fetch raw HTML and render nothing. A client-only SPA that Google eventually renders can be entirely invisible to the answer engines — you would rank, slowly, in the channel buyers are leaving while being uncitable in the one they are moving to.
Server-rendered HTML is the price of admission to AI search, and clean structured data plus an llms.txt manifest is the layer that makes a rendered page citable. That is the generative engine optimization work on top of indexability: first the content has to be in the HTML, then it has to be structured enough to lift.
The bottom line
Single-page applications are not bad for SEO, but client-only rendering is — it hides your content from every crawler that will not run your JavaScript, and a growing share of them, especially the AI engines, will not. The fix is to send real HTML: server-side rendering for dynamic content, static prerendering for stable pages, dynamic rendering only as a legacy bridge — plus real per-route URLs, metadata, structured data, and status codes. On Shopify this is a non-issue on a standard Liquid theme and a first-class concern the moment you go headless, which is why Hydrogen server-renders by default. Render on the server, and your SPA is as indexable and citable as any classic site.
RankEngine audits the SEO and AI-search signals of your storefront — meta, schema, indexing, llms.txt, and AI-crawler access — and applies verified fixes, whether you run a standard Shopify theme or a headless build. For headless stores, get the rendering right first, then let RankEngine handle the on-page and AI-search layer on top.
RankEngine