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 #3 in JavaScript SEO#267 in Technical SEO#360 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 renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM.: they ship a near-empty HTML shell and build the DOM in the browser. The SEO problem is identical across all five — crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. get an empty shell, and content only exists after JS executes. Google renders these apps but on a delayed, queued basis; Bing and AI crawlersAI crawlers are bots from AI companies that fetch web pages to train language models, build AI-search indexes, or answer live user questions. They come in three categories, each with its own user-agent tokens and its own robots.txt controls. are much less dependable. The fix is also identical: (1) head management (a per-route package for <title>/meta) and (2) a renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. 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 indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. 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 crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index.-side JavaScript execution without guaranteeing indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.. 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 linksAn internal link is a hyperlink from one page on a website to another page on the same website. Internal links help search engines discover your pages and pass ranking signals (PageRank and anchor-text context) between them., structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding. — 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 prerenderThe Speculation Rules API is a Chromium browser API (Chrome/Edge 109+) that lets a site tell the browser which same-site pages to prefetch (download the HTML document) or prerender (fully load and render in an invisible tab) before a visitor clicks — so the next navigation can be near-instant. It's a browser-side performance feature for real users, not a crawling, indexing, or ranking signal. 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

TIP Measure each framework route by its output

This raw-to-rendered delta demonstrates JavaScript dependency without declaring the framework itself unindexable. Rendering mode and route implementation are what matter.

Use my Render Gap Analyzer on representative public routes to compare initial and browser-built content, links, metadata, and directives. Render Gap Analyzer Free

  1. Test content, category, product, and error routes rather than only the home page.
  2. Compare raw and rendered content, links, titles, canonicals, robots directives, and schema.
  3. Move critical output to SSR or prerendering and rerun the same route to prove the gap closed.
Framework labels do not determine crawlability; delivered HTML does.

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 crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor. — 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 crawlersGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer. aren’t a given. Bing renders JavaScript inconsistently, and most AI crawlersAI crawlers are bots from AI companies that fetch web pages to train language models, build AI-search indexes, or answer live user questions. They come in three categories, each with its own user-agent tokens and its own robots.txt controls. (the bots fetching pages for LLMs and AI searchAI search uses large language models and retrieval-augmented generation (RAG) to synthesize an answer from multiple sources rather than returning a ranked list of links. Examples include Google AI Overviews, ChatGPT Search, and Perplexity.) 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 tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored. — 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 SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. 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 hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers.
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 SEOReact SEO is the practice of making React apps crawlable and indexable. React renders client-side by default, so the raw HTML is near-empty until JavaScript runs — SSR or SSG puts the content back in the initial response where crawlers (and AI bots) can reliably see it. — why CSR-first React creates indexing risk, React Router and the History API, react-helmet-async, and when to reach for Next.js.
  • Vue SEOVue SEO is the practice of making web apps built with Vue.js crawlable, renderable, and indexable. Because Vue 3 defaults to client-side rendering, the content isn't in the raw HTML — so router mode, head management, and a rendering strategy (CSR, prerendering, SSR, or SSG) all matter. — Vue 3’s CSR default, createWebHistory(), @unhead/vue, prerendering without a meta-framework, and when Nuxt is the right call.
  • Angular SEOAngular SEO is the practice of making Angular single-page apps crawlable, renderable, and indexable — chiefly by serving real HTML through server-side rendering (@angular/ssr) or prerendering instead of relying on client-side rendering, plus managing titles, meta tags, and clean URLs. — Angular’s SPA defaults, @angular/ssr, the built-in Title and Meta services, prerendering, and incremental hydration.
  • Svelte SEOSvelte SEO is the practice of making sure search engines and AI crawlers can see content built with Svelte. Plain Svelte is client-side rendered (a blank shell), so the key is to use SvelteKit, which renders pages on the server by default. — Svelte vs SvelteKit, SSR by default in SvelteKit, <svelte:head>, the adapter-static + ssr: false trap, and AI-crawler implications.
  • SolidJS SEOSolidJS SEO is the set of practices for making SolidJS apps crawlable, indexable, and fast. Bare SolidJS renders client-side (an empty HTML shell), so SEO depends on using SolidStart for SSR or SSG to put real content and metadata in the initial HTML. — 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 SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. hub.

Add an expert note

Pin an expert quote

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