SolidJS SEO

How to make SolidJS sites crawlable, indexable, and fast — why bare SolidJS is CSR-only, how SolidStart's SSR/SSG fixes it, managing head tags with @solidjs/meta, and why SolidJS hydrates (it is not resumable like Qwik).

First published: Jun 26, 2026 · Last updated: Jul 18, 2026 · Advanced
demand #8 in JavaScript Frameworks#49 in Platform SEO#285 in Technical SEO#381 on the site

SolidJS is a fast, React-like library with fine-grained reactivity and no virtual DOM — but on its own it's client-side rendered, so it ships an empty HTML shell with the same indexing problems as bare React. The fix is SolidStart, the official meta-framework: SSR or SSG put real content and metadata in the first response. Manage head tags with @solidjs/meta (<Title>, <Meta>, <Link>), return real 404s with <HttpStatusCode>, and fetch data on the server with createAsync. One thing not to mix up: SolidJS hydrates — it is not resumable. Resumability is Qwik's trick. SolidJS's win is a Core Web Vitals edge on top of standard SSR.

TL;DR — SolidJS is fine-grained reactive (signals, no virtual DOM) and ships as a CSR library by default — an empty shell with the same Wave-1 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. problem as bare React. SolidStart is the fix: SSR, SSG (route prerenderingTurning HTML, CSS, and JavaScript into the final visual page and DOM.), and streaming SSR put content and metadata in the first response. Manage head 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. with @solidjs/meta (<Title>, <Meta>, <Link> under a <MetaProvider>), return real status codes with <HttpStatusCode>, and load data on the server with createAsync so it lands in the SSR HTML. The accuracy spine: SolidJS hydrates — it is NOT resumable (that’s Qwik). Its fine-grained reactivity is a client update optimization, not a server-to-client startup one — so the SEO upside is mostly a Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. (INPInteraction to Next Paint — the input-to-paint latency at the 75th percentile of a page's interactions. ≤200 ms is good./TBTTotal Blocking Time — the sum of the blocking portion (time above 50 ms) of every long task between First Contentful Paint and Time to Interactive. It's a lab-recommended metric and the lab proxy for INP, not a Core Web Vital.) edge layered on top of normal SSR.

Evidence for this claim The article's described solidjs-seo capabilities must be evaluated against the platform's current documentation rather than assumed to be search-engine behavior. Scope: Platform-specific capability documentation. Confidence: high · Verified: SolidStart documentation Evidence for this claim Regardless of platform, Google needs crawlable URLs, accessible rendered content, descriptive metadata, and valid search directives. Scope: Google requirements independent of platform. Confidence: high · Verified: Google Search Central: SEO Starter Guide

SolidJS architecture: what’s actually different

SolidJS uses JSX that looks identical to React’s, plus hooks-like primitives (createSignal, createEffect, createMemo). But the execution model is fundamentally different:

  • React re-runs the whole component function when state changes and diffs a virtual DOM to decide what to update.
  • SolidJS compiles JSX to real DOM operations at build time. Components run once; reactive signals wire values directly to the specific DOM nodes that depend on them. When a signal changes, Solid updates only that node — no reconciliation, no virtual DOM overhead.

This is why SolidJS consistently sits near the top of the js-framework-benchmark suite, and why its runtime is dramatically leaner than React’s re-render model. The library itself is roughly 7KB gzipped. Smaller, surgical updates plus a tiny runtime is the performance story — but none of that changes the renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. mode, which is what decides your SEO fate.

CSR by default: the SEO problem

Build with SolidJS alone (no SolidStart) and you get a client-side-rendered app:

  1. The server sends index.html with a near-empty body — typically a <div id="root"></div> and a script tag.
  2. The browser downloads the JS bundle, runs Solid’s reactive code, and populates the DOM.
  3. 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.’s first wave sees the empty shell — no content, no metadata.
  4. 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.’s second wave (the rendering queue) eventually runs the JS and sees content — but the timing is unpredictable, from hours to weeks.
  5. Bing, social 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., and non-JS bots may never see the content at all.

This is the two-wave rendering process, and it’s the same trap any CSR SPA falls into. The practical impacts: slow initial 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. of new content; titles and meta descriptionsThe 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. missing from the raw HTML (so wrong/blank snippets in SERPs); and soft 404sA soft 404 is a URL that returns a success status code (usually 200 OK) even though the page is empty, missing, or shows a 'not found' message. It isn't a status code a server sends — it's a label search engines apply after comparing the response code against the rendered content, and they treat the page like a 404 for indexing., because a CSR app can’t return a true HTTP 404 — the server always answers 200 with the app shell.

SolidStart: the fix

SolidStart (1.0, stable) is the official SolidJS meta-framework, built on Vinxi (Vite + Nitro). It provides:

  • Isomorphic, file-based routing — files under src/routes/ map to URLs (src/routes/blog/[slug].tsx/blog/:slug). One correction worth being precise about: SolidStart itself does not ship a router or metadata library bundled in — per its own docs, “SolidStart itself does not ship with a Router or Metadata library. Rather, it leaves that open for you to use any library you want.” The routing and @solidjs/meta behavior described here come from adding @solidjs/router and @solidjs/meta explicitly (the official templates add both for you, which is why it can feel automatic — check your package.json rather than assuming they came free with the framework).
  • Multiple rendering modes, chosen per route: CSR, SSR (synchronous, asynchronous, or streaming), and SSG / route prerendering. A SolidStart project isn’t “SSR” or “CSR” as a whole — each route picks its own mode, so verify the rendering config (and the resulting HTML) for the specific route you care about rather than assuming the whole site inherited one setting.
  • Server functions — a "use server" directive for RPC-style server code (data fetching, database access).
  • Adapters for Cloudflare, Vercel, Netlify, Deno, Node, and static hosting.

Version note: this reflects SolidStart 1.0’s current documentation, which labels itself beta and was last updated 2026-04-28 — confirm details against the docs for the version you’re actually running before treating any of this as permanent API surface.

SSR vs. SSG vs. streaming — the SEO read

  • SSR (synchronous, asynchronous, or streaming — SolidStart names these as distinct sub-modes) delivers full content on the first request and is fully indexable on Wave 1. Best for frequently changing pages.
  • SSG (route prerendering) builds HTML at deploy time — the fastest TTFBTime to First Byte — the time from the start of a request to when the first byte of the response arrives. It's a diagnostic metric (not a Core Web Vital) and a major input to FCP and LCP; ≤0.8 s is good. and LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good., CDN-cacheable, no per-request server work. Best for blogs, docs, and marketing pages. Configure prerendered routes in app.config.ts:
// app.config.ts
import { defineConfig } from "@solidjs/start/config";

export default defineConfig({
  server: {
    prerender: {
      routes: ["/", "/about", "/blog"],
    },
  },
});
  • Streaming SSR sends HTML progressively to improve TTFB on data-heavy pages; Google renders the full streamed output.
  • CSR is fine for authenticated dashboards and tools that don’t need to rank — not for public content.

Managing head tags with @solidjs/meta

The @solidjs/meta package is the SolidJS equivalent of React Helmet or Next.js’s <Head>. It’s SSR-ready and asynchronous. Wrap your app in <MetaProvider> so tags are collected during SSR, then set per-page tags inside your route components:

// src/routes/about.tsx
import { Title, Meta, Link } from "@solidjs/meta";

export default function AboutPage() {
  return (
    <>
      <Title>About Us — My Site</Title>
      <Meta name="description" content="Learn more about us." />
      <Link rel="canonical" href="https://example.com/about" />
      <Meta property="og:title" content="About Us" />
      <Meta property="og:description" content="Learn more about us." />
      <Meta property="og:image" content="https://example.com/og-about.jpg" />
      <main>...</main>
    </>
  );
}

The components available are <Title>, <Meta>, <Link>, <Style>, <Base>, and the <MetaProvider> wrapper. Deduplication is built in: <Meta> tags with matching name attributes override parent definitions — the deepest (most specific) one wins, and this works correctly during SSR. So a root default title and per-page overrides behave as you’d expect.

One warning straight from the docs: don’t add raw <title> tags in your server files — they override @solidjs/meta’s functionality. Always use the <Title> component, never a hand-written HTML title in your server template.

Structured data (JSON-LD)

Google supports JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. whether it’s in the raw HTML or injected by JavaScript, but SSR-rendered JSON-LD is the reliable choice. In SolidStart, render it in the route component:

export default function ArticlePage() {
  const schema = {
    "@context": "https://schema.org",
    "@type": "Article",
    "headline": "My Article",
    "author": { "@type": "Person", "name": "Author Name" },
  };

  return (
    <>
      <script
        type="application/ld+json"
        innerHTML={JSON.stringify(schema)}
      />
      <article>...</article>
    </>
  );
}

With SSR enabled, the JSON-LD appears in the server-rendered HTML — the preferred approach for reliability across every 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.. Validate with the Rich ResultsRich results (formerly 'rich snippets') are enhanced search listings — stars, images, prices, breadcrumbs, video thumbnails, and more — that Google and Bing build from structured data. They're a display feature, not a ranking factor, and eligibility never guarantees they'll show. Test or URL Inspection ToolA 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..

Hydration, not resumability — the accuracy spine

HydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. is the process where, after SSR delivers rendered HTML, the framework attaches to the existing DOM to make it interactive: download the JS bundle, execute the framework code, reconcile state with the existing nodes, attach event listeners. SolidJS uses hydration — the same fundamental mechanism as React’s hydrate, Vue’s createSSRApp, or Angular SSR.

Resumability (Qwik’s architecture) is different: the server serializes the framework’s execution state into the HTML, and the client resumes from there without re-executing components — interactive before any JS runs.

The myth to debunk: SolidJS’s fine-grained reactivity is sometimes confused with resumability because both avoid “re-running” components in the traditional sense. They are not the same thing. Fine-grained reactivity is a client-side update optimization; resumability is a server-to-client startup mechanism. SolidJS still downloads and runs its runtime before the page is interactive.

The SEO implication: both SSR+hydration (SolidJS) and resumability (Qwik) put rendered HTML in the server response, so both are good for indexing. The difference is Time to Interactive and INPInteraction to Next Paint — the input-to-paint latency at the 75th percentile of a page's interactions. ≤200 ms is good. on slow devices, where Qwik’s near-zero startup JS has the edge. For crawl and index, SolidStart SSR is already enough.

Core Web Vitals

SolidJS’s runtime advantages can translate into better Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. scores on routes that use SSR — but this isn’t automatic or universal. No primary source establishes guaranteed cheap hydration or better outcomes for every Solid/SolidStart route: bundle size, data fetching, third-party scripts, device, and the route’s actual rendering mode all determine the real cost. Treat the following as the mechanism by which SolidJS can help, and measure your own routes to confirm it does:

  • LCP improves because the largest content element is in the server-rendered HTML — no waiting on JS.
  • INP improves because Solid’s fine-grained runtime handles interactions efficiently (no virtual DOM diffing).
  • CLSCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good. drops because SSR’d content doesn’t shift as late-loading JS fills in.

A caution worth keeping honest: figures like “SolidJS is ~70% faster than React’s virtual DOM” come from synthetic benchmarks (js-framework-benchmark), not real-world field dataPerformance metrics captured from real users, not lab tests.. Real CWV depends far more on implementation quality, data fetching, and hosting than on framework choice. Treat the benchmark numbers as directional, and measure your own field data in CrUXChrome User Experience Report — Google's public dataset of real-world (field) performance data from eligible Chrome users. It's the official field-data source behind the Core Web Vitals program. / Search ConsoleGoogle's free tool for monitoring crawling, indexing, and search performance..

Routing, data fetching, and real 404s

Routing. SolidStart uses the History API by default — never use hash-based (#/page) routing, which prevents reliable URL discoveryURL discovery is how search engines find URLs to crawl — by pull (following links and reading sitemaps) and by push (you notify them via IndexNow, the Indexing API, or WebSub). It's the find step that comes before a page is ever fetched.. Keep trailing-slash behavior consistent and enforce it with server redirectsA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't., not just canonicals.

Data fetching for SEO. Content that needs to rank must be loaded on the server so it’s in the SSR HTML. Use createAsync and server functions — not onMount or client-side effects, which run after the HTML is sent:

// Server-side data fetch — content lands in the initial HTML ✓
import { createAsync } from "@solidjs/router";
import { getPost } from "~/lib/api";

export const route = {
  load: ({ params }) => getPost(params.slug),
};

export default function BlogPost(props) {
  const post = createAsync(() => getPost(props.params.slug));
  return <article>{post()?.content}</article>;
}

Real 404s. A catch-all route plus <HttpStatusCode> from @solidjs/start sets the actual HTTP response code on the server — a true 404 instead of a soft 404:

// src/routes/[...404].tsx
import { HttpStatusCode } from "@solidjs/start";

export default function NotFound() {
  return (
    <>
      <HttpStatusCode code={404} />
      <h1>Page Not Found</h1>
    </>
  );
}

Sitemaps, robots.txt, and international SEO

  • robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere. — add public/robots.txt at the project root and point it at your sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing.. Don’t block the /api/ routes used for XHR data fetching.
  • Sitemap — the third-party solid-start-sitemap plugin (by madaxen86) generates sitemap.xml at build time and supports dynamic routes via parameter mapping or a runtime API route.
  • International SEOInternational SEO is the practice of optimizing a site so search engines understand which countries and/or languages it targets, and serve the right version to each user. It spans URL structure, hreflang, and on-page localization. — emit hreflangHreflang is an annotation (in HTML, HTTP headers, or XML sitemaps) that tells search engines which language and optional region a page targets, and which alternate versions exist. It only works when every page in the cluster references all the others. with <Link rel="alternate" hreflang="…"> from @solidjs/meta, and use a subdirectory structure (/en/, /fr/) over subdomains or query parameters.

Common SolidJS SEO pitfalls

  1. Running SolidJS CSR without SolidStart SSR/SSG.
  2. Fetching rankable content in onMount / client-side effects.
  3. Forgetting the <MetaProvider> wrapper (tags won’t render in SSR).
  4. Adding raw <title> HTML tags in server templates (overrides @solidjs/meta).
  5. Using hash-based routing.
  6. Serving soft 404s without <HttpStatusCode>.
  7. Blocking API routes in robots.txt.
  8. Not fingerprinting JS assets (Google caches scripts aggressively).
  9. Content behind a click/accordion/tab that never auto-loads into the DOM.
  10. Assuming SSR works without verifying via View Source / URL Inspection.
  11. Assuming @solidjs/router and @solidjs/meta come bundled with SolidStart — they don’t; SolidStart’s own docs say it “does not ship with a Router or Metadata library.” Official templates add both for you, but a from-scratch or customized setup needs them installed explicitly.
  12. Treating a project as uniformly “SSR” or “CSR” — rendering mode is set per route in SolidStart, so a route you assume is server-rendered may not be.

Where this sits

SolidJS lives in the JavaScript frameworksJavaScript frameworks — React, Vue, Angular, Next.js, Nuxt, Svelte, Astro — are libraries or meta-frameworks for building web UIs. Their SEO impact depends on rendering mode: SSR and SSG deliver pre-rendered HTML; CSR-only apps require Googlebot to execute JavaScript before it can index content. subcluster alongside Qwik, under the broader JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. cluster. For the closest parallel, see 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. — SolidJS shares React’s JSX and CSR-by-default problem, but fixes it with SolidStart instead of Next.js, and runs faster thanks to no virtual DOM. For how Google sees rendered content, see renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. and 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..

Add an expert note

Pin an expert quote

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