Client-Side JavaScript Frameworks

React, Vue, Angular, Svelte, and SolidJS share one SEO problem — client-side rendering ships an empty shell — and one fix pattern: head management plus a rendering strategy.

First published: Jun 26, 2026 · Last updated: Jul 17, 2026 · Advanced
demand #5 in JavaScript SEO#271 in Technical SEO#378 on the site

React, Vue, Angular, Svelte, and SolidJS are UI libraries that, by default, ship a near-empty HTML shell and build the page in the browser (client-side rendering). That means crawlers get a blank page until JavaScript runs — Google can render it, but on a delayed queue, and other crawlers (Bing, AI bots) are far less reliable. The fix is the same across all five: (1) a head-management package so meta tags exist for each route, and (2) a rendering strategy — prerendering, SSR, or moving to the matching meta-framework. This hub explains the shared problem and the shared fix, then points you to the per-framework deep dives.

TL;DR — React, Vue, Angular, Svelte, and SolidJS are all UI libraries that default to client-side rendering: they ship a near-empty HTML shell and build the DOM in the browser. The SEO problem is identical across all five — crawlers get an empty shell, and content only exists after JS executes. Google renders these apps but on a delayed, queued basis; Bing and AI crawlers are much less dependable. The fix is also identical: (1) head management (a per-route package for <title>/meta) and (2) a rendering strategy (prerendering, SSR, or migrating to the matching meta-framework). The differences between the frameworks are mostly which package and which strategy — covered in each deep dive below.

They’re libraries, not full frameworks — and that’s the root of it

Framework architecture varies, so client rendering is not an automatic indexing failure. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: MDN: Client-side frameworks Server rendering or prerendering reduces dependence on crawler-side JavaScript execution without guaranteeing indexing. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: Google: JavaScript SEO basics

React, Vue, Angular, Svelte, and SolidJS are primarily UI rendering libraries. Their job is to turn your data into a DOM and keep it in sync as state changes. Out of the box, that DOM is built in the browser — which is exactly what makes them feel fast and app-like, and exactly what creates the SEO problem.

A default build ships an HTML shell with an empty mount node (<div id="root">, <div id="app">, <app-root>) and a JavaScript bundle. Everything a search engine cares about — headings, body copy, internal links, structured data — is injected by that bundle after it downloads and executes. Before JS runs, there’s nothing there.

This is client-side rendering — the out-of-the-box behavior for the bare libraries before you add a meta-framework or a build-time prerender step. It’s not a bug; it’s the default you start from. But “default” isn’t “permanent” or “universal”: the default changes by version and by which CLI or starter you used, and two routes in the same app can end up on different rendering modes depending on how each one is built (a marketing page prerendered, a dashboard left as pure CSR). Don’t assume a framework’s behavior from its name — check what a given route in a given build actually ships.

The shared problem: an empty shell

Google is explicit that it processes JS apps in phases — crawl, render, index — and that “without rendering Google might not see that content.” For a CSR app, all the content lives behind that render step. Three consequences fall out of that:

  • The render is delayed, and the wait isn’t fixed. Rendering is a separate, queued step after crawling — Google documents crawl, render, and index as three distinct phases, but it doesn’t commit to one universal wait time for every page. The delay varies by URL and by Google’s own resourcing at the time; your content doesn’t exist for indexing until the render actually completes, however long that takes for that page.
  • Non-Google crawlers aren’t a given. Bing renders JavaScript inconsistently, and most AI crawlers (the bots fetching pages for LLMs and AI search) do little or no JS execution. These are separate companies with separate infrastructure, so none of it generalizes from Google’s documentation — verify each provider’s current behavior rather than assuming it matches Google’s or last year’s test. A CSR-only site risks being near-invisible to whichever crawlers don’t render, and AI crawlers are a growing share of traffic.
  • Per-page metadata is missing. One HTML file means one <title> and one meta description for every route unless you actively manage the head per route.

The shared fix, part 1: head management

Because a SPA has a single HTML document, you need code that updates the document head as the user (and the crawler’s renderer) moves between routes. Every framework has a canonical package or built-in API for this:

  • Reactreact-helmet-async (or the framework’s metadata API if you’ve moved to Next.js).
  • Vue@unhead/vue (the engine behind Nuxt’s SEO utilities).
  • Angular → the built-in Title and Meta services from @angular/platform-browser — no extra dependency.
  • Svelte → the built-in <svelte:head> element.
  • SolidJS@solidjs/meta (and SolidStart for the SSR path).

Head management gets you correct, unique meta tags — but it does not solve the empty-shell problem on its own. The tags still only appear after JS runs. That’s why you also need part 2.

The shared fix, part 2: a rendering strategy

To put real content (and head tags) into the HTML before JavaScript executes, you choose one of three approaches. The ranking, by how much SEO risk they remove:

  1. Prerendering / static generation (SSG). Build static HTML for each route at build time. Lowest risk for content that doesn’t change per request. Each library has a prerender path (e.g. SolidJS’s built-in prerender, Vue prerender plugins, Angular’s prerendering).
  2. Server-side rendering (SSR). Render the HTML on the server per request, then hydrate in the browser. This is where the meta-frameworks come in — Next.js (React), Nuxt (Vue), Angular SSR (formerly Angular Universal), SvelteKit (Svelte), and SolidStart (SolidJS). For most content sites that must rank, adopting the matching meta-framework is the cleanest fix.
  3. Stay full CSR, but make it crawlable. Sometimes acceptable for app-like surfaces behind a login that don’t need to rank — but a poor default for anything you want in search.

Make this call per route, not per app. A marketing page and a logged-in dashboard in the same codebase can reasonably land on different rows of that list. For each route, ask:

  • Output — does the content need to be in the initial response, or is it fine to appear after JS runs?
  • Freshness — is it stable enough to build once (prerender), or does it change per request (SSR)?
  • Personalization — is it different per visitor? Prerendering can’t help there; you’re choosing between SSR and CSR.
  • Server cost — SSR adds compute on every request; prerendering front-loads that cost at build time instead.
  • JS dependence — how much of the route’s usefulness depends on client JavaScript running at all?
  • Failure behavior — if JS fails or is blocked, does the route degrade to something usable, or to nothing?

The decision is the same conversation regardless of framework: does this content need to rank or be cited? If yes, get it into the server-rendered or prerendered HTML. If it’s purely interactive app UI, CSR is fine.

How Google handles these apps (and why others don’t)

Google can render every one of these frameworks — it runs an evergreen, headless Chrome and executes your JS. But it does so on a deferred queue, and the renderer is stateless (no persisted cookies/localStorage, no service workers, no interaction — it won’t scroll or click). So the same JS-SEO rules from the JavaScript SEO hub apply on top of the framework choice: real <a href> links, parity between raw and rendered DOM, no content gated behind interaction, no JS-injected noindex.

Outside Google, the picture is worse. Bing’s JS rendering is patchier, and the AI crawlers largely don’t render at all. For a CSR app, that can mean your content exists for Google (eventually) but not for Bing or for the AI tools that an increasing number of people search with. SSR or prerendering closes that gap for everyone, not just Google — which is the strongest argument for not shipping CSR-only content.

The five frameworks at a glance

FrameworkDefaultHead managementSSR / meta-frameworkSEO note
ReactCSRreact-helmet-asyncNext.js (Metadata API)The most common CSR-first SPA; Next.js is the standard SEO fix
VueCSR@unhead/vueNuxt (useSeoMeta())Nuxt is SSR-by-default; plain Vue needs prerender or Nuxt
AngularCSR (SPA)built-in Title/Meta servicesAngular SSR (was Universal)Big SPAs; modern Angular adds SSR + incremental hydration
SvelteCSR (Svelte) / SSR (SvelteKit)<svelte:head>SvelteKit (SSR default)SvelteKit is SSR by default — watch the ssr:false static trap
SolidJSCSR@solidjs/metaSolidStartFine-grained reactivity; SolidStart adds SSR + prerender

The pattern is consistent: a CSR default, a head package, and a rendering strategy (usually the matching meta-framework). What differs is the names and a few sharp edges.

Where to go next: the framework deep dives

This hub is the map. Each framework has its own guide with the specific packages, config, and gotchas:

  • React SEO — why CSR-first React creates indexing risk, React Router and the History API, react-helmet-async, and when to reach for Next.js.
  • Vue SEO — Vue 3’s CSR default, createWebHistory(), @unhead/vue, prerendering without a meta-framework, and when Nuxt is the right call.
  • Angular SEO — Angular’s SPA defaults, @angular/ssr, the built-in Title and Meta services, prerendering, and incremental hydration.
  • Svelte SEO — Svelte vs SvelteKit, SSR by default in SvelteKit, <svelte:head>, the adapter-static + ssr: false trap, and AI-crawler implications.
  • SolidJS SEO — SolidJS’s CSR default, @solidjs/meta, SolidStart for SSR and prerendering, and how its fine-grained model affects rendered output.

For the rendering modes themselves (CSR, SSR, SSG, ISR, hydration, dynamic rendering) and the broader render/parity rules, see the parent JavaScript SEO hub.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin an expert quote first.