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.
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 tools developers use to build interactive sites. By default, they send the browser a nearly empty HTML page and then build the real content with JavaScript. That’s great for users but risky for SEO — a search 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. can land on a blank page. The fix is the same for all of them: manage your
<title>and 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. per page, and make sure your content exists in the HTML before JavaScript runs.
What client-side frameworks are
Client-side frameworks can render route content in the browser after an initial HTML response. 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 Google can render JavaScriptMaking sure search engines can crawl, render, and index content that depends on JavaScript., but resource availability and renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. still affect what is processed. 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
A client-side framework is a JavaScript library that builds the page in your visitor’s browser. The five you’ll run into most are React, Vue, Angular, Svelte, and SolidJS. They’re how most modern web apps and dashboards get built.
The catch is in the word client-side. By default, these frameworks ship a tiny HTML file that looks roughly like this:
<body>
<div id="root"></div>
<script src="/bundle.js"></script>
</body>There’s no content in that HTML — just an empty container and a script. The browser downloads the script, runs it, and then the headline, the text, the links, and everything else appear. This default behavior is called client-side renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. (CSR), and a site built this way is often called a single-page application (SPA).
Why that’s a problem for SEO
A search 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. is not a person clicking around. When GooglebotGooglebot 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. or another crawler fetches your page, the first thing it gets is that empty shell. If the crawler doesn’t run your JavaScript, it sees a blank page — no content to indexStoring 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..
Google can run JavaScript, so it usually does eventually see your content. But:
- It happens on a delay (rendering is a separate, queued step).
- Other crawlers — Bing and the 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. feeding tools like ChatGPT — are far less reliable at running JavaScript.
So “it works in my browser” doesn’t mean “search engines can see it.”
The fix is the same for all five
No matter which framework you picked, the solution comes in two parts:
- Manage the head. Each page needs its own
<title>and meta descriptionThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through.. A plain CSR app has one HTML file, so without help every page shares the same title. Each framework has a small package that fixes this (more in the Advanced tab). - Pick a rendering strategy. Get your content into the HTML before JavaScript runs — by prerendering (building static HTML ahead of time), server-side rendering (SSR), or moving to the framework’s matching meta-framework (Next.js for React, Nuxt for Vue, and so on).
Want the per-framework specifics — which head package, which rendering option, and how Google actually handles these apps? Switch to the Advanced tab, then jump to the dedicated guide for your framework.
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
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
- Test content, category, product, and error routes rather than only the home page.
- Compare raw and rendered content, links, titles, canonicals, robots directives, and schema.
- Move critical output to SSR or prerendering and rerun the same route to prove the gap closed.
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:
- React →
react-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
TitleandMetaservices 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:
- 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).
- 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.
- 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
| Framework | Default | Head management | SSR / meta-framework | SEO note |
|---|---|---|---|---|
| React | CSR | react-helmet-async | Next.js (Metadata API) | The most common CSR-first SPA; Next.js is the standard SEO fix |
| Vue | CSR | @unhead/vue | Nuxt (useSeoMeta()) | Nuxt is SSR-by-default; plain Vue needs prerender or Nuxt |
| Angular | CSR (SPA) | built-in Title/Meta services | Angular SSR (was Universal) | Big SPAs; modern Angular adds SSR + incremental hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. |
| Svelte | CSR (Svelte) / SSR (SvelteKit) | <svelte:head> | SvelteKit (SSR default) | SvelteKit is SSR by default — watch the ssr:false static trap |
| SolidJS | CSR | @solidjs/meta | SolidStart | Fine-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-inTitleandMetaservices, 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>, theadapter-static+ssr: falsetrap, 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.
AI summary
A condensed take on the Advanced version:
- One shared starting point. React, Vue, Angular, Svelte, and SolidJS
default to client-side renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. out of the box — a near-empty HTML shell
(
<div id="root">+ a bundle) that builds the DOM in the browser. That default changes by version, starter, and meta-framework, and can vary route by route within the same app — check what a given route actually ships rather than assuming from the framework name. - Google renders it, but the wait isn’t fixed. RenderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. 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., but Google doesn’t commit to one universal delay for every page — it varies by URL. The renderer is also stateless and won’t scroll/click.
- Other 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. aren’t a given. Bing renders JS inconsistently; most AI 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. don’t render at all. These are separate companies, so none of this generalizes from Google’s docs — verify each provider’s current behavior.
- One shared fix, two parts. (1) Head management — a per-route package for
<title>/meta:react-helmet-async(React),@unhead/vue(Vue), built-inTitle/Meta(Angular),<svelte:head>(Svelte),@solidjs/meta(SolidJS). (2) A rendering strategy — 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./SSG, SSR, or move to the matching meta-framework (Next.js / Nuxt / Angular SSR / SvelteKit / SolidStart). - Head management alone isn’t enough — the tags still only appear after JS runs; you still need a rendering strategy to fill the shell.
- Decide per route, weighing output timing, freshness, personalization, server cost, JS dependence, and failure behavior — not per app. If a route needs to rank or be cited, get it into server-rendered or prerendered HTML. If it’s app-only UI behind a login, CSR is fine.
Official documentation
Primary-source documentation from the search engines and the frameworks.
- Understand the JavaScript SEO basics — the crawl → render → indexStoring 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. phases, crawlable links, and testing rendered HTML.
- Fix Search-related JavaScript problems — soft-404 handling after client-side routing, the History API, and renderer constraints.
- In-Depth Guide to How Google Search Works — where renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. sits in crawl → index → serve.
Bing / Microsoft
- bingbot Series: JavaScript, Dynamic Rendering, and Cloaking. Oh My! — Bing’s take on renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. JS and why dynamic rendering exists.
The frameworks (head + rendering)
- React docs — the library that popularized the CSR-first SPA; see also Next.js metadata.
- Vue.js — Rendering / SSR and Nuxt SEO utilities.
- Angular — Server-side rendering and the
Title/Metaservices. - SvelteKit — Page options (SSR/prerender) and
<svelte:head>. - SolidStart — SSR & prerendering and
@solidjs/meta.
Quotes from the source
On-the-record statements that ground the shared CSR problem and fix. Each search-engine link is a deep link that jumps to the quoted passage.
Google — renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. & why the shell matters
- “Google processes JavaScript web apps in three main phases: 1. 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. 2. RenderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. 3. 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..” Jump to quote
- “Rendering is important because websites often rely on JavaScript to bring content to the page, and without rendering Google might not see that content.” Jump to quote
- “Google can only discover your links if they are <a> HTML elements with an href attribute.” — applies to every framework’s router. Jump to quote
- “Google Search does not interact with your page.” — the renderer won’t scroll or click to reveal content. Jump to quote
Patrick Stox (my own work — JavaScript SEO: A Definitive Guide)
- “Any kind of SSR, static rendering, and prerendering setup is going to be fine for search engines.” — the through-line for every framework on this hub.
- The renderer takes the most restrictive robots directive across raw and rendered HTML — so a framework that injects
noindexclient-side can deindexDeindexing means getting a URL to stop appearing in Google's search results. There's no single delete button — the right method depends on whether you own the page, whether removal is temporary or permanent, and whether the content should still exist. a page even when the shell saysindex.
Client-side framework SEO checklist
Works for React, Vue, Angular, Svelte, and SolidJS alike:
- You know whether your app is CSR, SSR, or prerendered today (check View Source — is content in the raw HTML, or just an empty mount node?).
- Content that must rank or be cited is in the server-rendered or prerendered HTML — not injected only after JS runs.
- Per-route
<title>and meta descriptionThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through. are managed (head package or built-in API), and they’re unique per page in the rendered HTML. - Router links are real
<a href>anchors (History API routing, not#-fragment routing oronclickon a<div>). - JavaScript and CSS are not blocked in
robots.txt(Google won’t render from blocked files). - No content is gated behind a scroll, click, or hover.
- Client-side “not foundA 302 (\"Found\") is a temporary redirect: it forwards users to a new URL while telling search engines the original URL should stay in the index. It's a weak canonicalization signal, not the zero-equity dead end of SEO folklore.” views return a real
404or carrynoindex(no soft-404 shells). - No JS injects a
noindexthat contradicts the raw HTML (Google takes the most restrictive). - You’ve considered 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., not just Google — SSR/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. is the only reliable answer for them.
- You’ve verified in URL InspectionA Google Search Console feature that reports how Google sees one specific URL on a property you own. By default it shows the last-indexed snapshot; a separate \"Test live URL\" mode fetches the current version. (rendered HTML + screenshot + console), not just your own browser.
Those checks collapse several distinct outputs into one page. Verify each stage separately — a route can pass one and fail the next:
| Stage | What to check |
|---|---|
| Direct response (JS disabled/blocked) | Fetch the URL with JS off. Is your unique heading and body copy present, or just the mount node? |
| Streamed chunks (if streaming) | Does content arrive progressively, or does a slow chunk hold up everything after it? |
| Rendered DOM (JS executed) | After the bundle runs, is the final DOM complete — headings, links, 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. all present? |
| HydrationTurning HTML, CSS, and JavaScript into the final visual page and DOM. | Does the client take over cleanly, or do console errors/hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. mismatches appear? |
| Client navigation | Does an in-app route change update the URL, title, and canonical the same way a direct load would? |
| Status codes | Does a real 404/410/5xx route return that status directly, not just a client-rendered message inside a 200? |
| Metadata | Is the rendered <title>, description, canonical, and robots directive correct per route — not just present? |
Frameworks → fix, at a glance
| Framework | Default render | Head management | SSR / meta-framework | 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. path |
|---|---|---|---|---|
| React | CSR | react-helmet-async | Next.js | Next.js SSG / react-snap |
| Vue | CSR | @unhead/vue | Nuxt | Nuxt nuxi generate / prerender plugin |
| Angular | CSR (SPA) | built-in Title / Meta | Angular SSR (was Universal) | ng build prerender |
| Svelte | CSR (Svelte) / SSR (SvelteKit) | <svelte:head> | SvelteKit | adapter-static (mind ssr:false) |
| SolidJS | CSR | @solidjs/meta | SolidStart | SolidStart prerender |
The two-part fix (memorize this)
- Head management → unique
<title>/ meta per route. - RenderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. strategy → prerender (SSG) or SSR or the matching meta-framework. Head management alone does not fill the empty shell.
Risk ladder (lowest → highest SEO risk)
| Approach | SEO risk | When |
|---|---|---|
| Prerender / SSG | Lowest | Content stable per request |
| SSR (meta-framework) | Low | Per-request / dynamic content that must rank |
| HydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. (isomorphic) | Low | App + content hybrid |
| Full CSR | Highest | App UI behind login that needn’t rank |
Crawler reality check
| 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. | Runs your JS? |
|---|---|
| GooglebotGooglebot 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. | Yes — but delayed, queued, stateless |
| BingbotBingbot is Microsoft Bing's primary web crawler — the bot that discovers, fetches, and renders pages to build the Bing index. That index also powers Yahoo, DuckDuckGo, Ecosia, and Microsoft Copilot, so Bingbot's reach is far wider than Bing's own search-market share. | Inconsistently |
| 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. (LLMA large language model (LLM) is a deep-learning model trained on massive text corpora to predict the next token and generate human-like text. LLMs use the transformer architecture and power AI search features like Google's AI Overviews (Gemini) and Bing Copilot (GPT-4). / 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.) | Mostly no |
CSR-only content is a gamble everywhere except Google — and even there it’s delayed. SSR/prerender removes the gamble for all of them.
Common client-side framework SEO issues
Search tools show an empty page or only the app shell
Symptom: Raw HTML contains only a mount element such as #root, #app, or
app-root, while the visible page contains the real copy. Likely cause: The route
is fully client-rendered. Fix: 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. stable routes or move the route to the
framework’s SSR-capable meta-framework. Confirm the fix by fetching the URL with
JavaScript disabled and finding its unique heading and body copy in the response.
Every route has the same title or canonical
Symptom: Several URLs render different views but expose the same title, description, or canonical. Likely cause: The shared HTML shell owns the head and route changes do not update it. Fix: Use the framework-specific head API and set metadata from route data. Confirm each route’s final DOM contains one self-referencing canonical and its own title.
Links work for users but crawlers do not discover routes
Symptom: Navigation works in the browser, yet linked routes stay undiscovered.
Likely cause: Click handlers on buttons or div elements replace crawlable
anchors. Fix: Render real <a href="..."> links and let the router enhance them.
Confirm the destination appears as an href in the rendered DOM without clicking.
Console shows hydration errors or mismatched content
Symptom: The server-rendered/prerendered HTML looks right, but the browser
console logs hydrationTurning HTML, CSS, and JavaScript into the final visual page and DOM. warnings, or content briefly flashes and changes right
after load. Likely cause: The server output and the client’s first render
disagree — often from date/locale formatting, random IDs, or window-dependent
code running during the initial render. Fix: Make the server and client
render deterministically for the same input; move browser-only logic to run
after hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers., not during it. Confirm by loading the route with JS enabled and
checking for zero hydration errors in the console.
A route changes state in the browser but the direct URL doesn’t match
Symptom: Clicking through the app produces a working page, but requesting that same URL directly (or after a refresh) returns a different result — a generic shell, a wrong status code, or stale metadata. Likely cause: Client navigation updates the in-browser view without a matching server-side route handler, so state that exists after a client transition was never a real, independently-requestable page. Fix: Make sure every route a user can reach by navigating is also directly requestable and returns the same content, status, and metadata. Confirm by loading the URL fresh (not by clicking into it) with JS disabled first, then enabled.
Test yourself: Client-side frameworks
Five quick questions on the shared SEO problem and fix across React, Vue, Angular, Svelte, and SolidJS. Pick an answer for each, then check.
Resources worth your time
My related writing
- JavaScript SEO: A Definitive Guide — my full guide to renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM., DOM parityWhether the rendered DOM matches what you expect the raw HTML to become., the most-restrictive-directive rule, and choosing a renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. setup. The foundation under every framework on this hub.
- The Beginner’s Guide to Technical SEO — where client-side rendering and JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. fit in the bigger picture.
My speaking
- How Search Works (SlideShare) — my walkthrough of 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., rendering, 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., and ranking. (My standing disclaimer applies: “This is my understanding of systems… not going to be 100% complete or accurate.”)
From around the industry
- web.dev — Rendering on the Web — the Chrome team’s canonical explainer of CSR vs. SSR vs. SSG vs. hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers.; the best framework-agnostic mental model for the rendering strategy half of the fix.
- Google Search Central — JavaScript SEO basics — official primary-source docs on the crawl → render → index process and crawlable links.
- Google Search Central — Fix search-related JavaScript problems — soft-404s after client-side routing and the History API, which every SPA router hits.
- Martin Splitt’s JavaScript SEO playlist — Google’s official video series on how it handles JS apps, framework-agnostic.
- Onely — JavaScript SEO hub — deep technical posts on rendering and JS-SEO auditing from a specialist agency.
Build-time retrieval analysis plus live signals for this exact article. The automatic chunk report includes a deterministic readiness score and is ready without a model download.
Search Console
sampleGA4 traffic (28d)
sampleCloudflare traffic (7d)
sampledCrUX field data (28d, phone)
sampleGoogle NLP entities
localChangelog
Updated Jul 17, 2026.
Editorial summary and recorded change details.Summary
Corrected the universal-shell and fixed-delay framing per the brief's evidence review, added a per-route rendering decision list, and expanded the checklist and troubleshooting sections with route/stage-level validation cases.
Change details
-
Reframed the empty-shell default as version/build-scoped rather than a universal fixed behavior, and removed the unsourced median-delay figure for Google's render queue.
-
Added a per-route decision list (output, freshness, personalization, server cost, JS dependence, failure behavior) to the rendering-strategy section.
-
Added a stage-by-stage validation table to the checklist and two new troubleshooting entries (hydration mismatches, client-navigation vs. direct-URL parity).
Full comparison unavailable — no prior snapshot was archived for this revision.