Qwik SEO

How Qwik's resumability puts content in the HTML by default and drives near-zero INP and TBT — plus the QwikCity patterns for meta tags, canonical URLs, JSON-LD, sitemaps, and i18n that make a Qwik site rank.

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
1 evidence signal on this page

Qwik is a JavaScript framework whose core trick is resumability: the server serializes app state, listeners, and the component tree into the HTML, so the browser resumes without hydrating (a ~1 KB Qwikloader script still runs — it's not literally zero JS). For SEO that's a double win on routes that are actually server-rendered — content is in the HTML by default (crawlers see it immediately, no render-wave delay) and INP/TBT trend toward zero because no hydration blocks the main thread on load; confirm both with field data rather than assuming them. QwikCity, the meta-framework, lets you mix SSR and SSG per route, plus routeLoader$() for server-side data and a head export for title/meta/OG/canonical/JSON-LD — all server-rendered. Basic SEO works out of the box; the main discipline is using routeLoader$() for dynamic metadata, not client-side effects. The catch is a smaller ecosystem than React/Vue, and — as of this writing — Qwik is on its v2 line, currently in beta.

TL;DR — Qwik’s core innovation is resumability: at SSR time it serializes event listeners (as HTML attributes), the component tree (in HTML comments), and app state (in a qwik/json script) into the HTML, so the browser resumes instead of hydrating — a ~1 KB Qwikloader script still runs, so it’s not literally zero JS. That’s categorically different from React/Vue/Angular/SolidJS, which all hydrate. On routes that are actually server-rendered, the SEO payoff is twofold: content is in the HTML by default (no render-wave delay for 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 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. trend toward zero because no hydrationTurning HTML, CSS, and JavaScript into the final visual page and DOM. blocks the main thread on load — verify both with field data rather than assuming them. QwikCity is the meta-framework — SSR and SSG can be mixed per route, not chosen once for the whole project. Metadata lives in the head export (title, meta, OG, canonical, 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.), fed by routeLoader$() for server-side data. SitemapsA 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. are automatic in SSG (for routes actually built) and a dynamic route in SSR. Qwik is currently on its v2 line (beta) as of this writing. The honest caveat: the ecosystem is newer and smaller than React’s.

Resumability: the core idea (and why SEO cares)

Qwik was built by Miško Hevery — the creator of Angular — at builder.io, and its whole reason to exist is to kill hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers.. (As of this writing, Qwik is on its v2 line, currently in beta — check the API surface against your installed version before copying code from any source, including this one.) Hydration is what every mainstream framework does after SSR: it downloads the component code and re-executes it in the browser to attach event handlers and rebuild internal state. As Hevery puts it, “Hydration is when an application is downloaded and executed twice, once as HTML and again as JavaScript.” That second execution is pure overhead — the page looks ready but isn’t interactive, and the main thread is blocked.

Qwik instead serializes three things into the HTML at SSR time:

  • Event listeners — encoded as HTML attributes (e.g. on:click="./chunk.js#symbol"). A tiny inline script (the Qwikloader, ~1 KB) attaches one global listener, reads the encoded chunk/symbol on an event, and downloads only that handler code on demand.
  • Component tree structure — encoded in HTML comments, so Qwik can rebuild the hierarchy without executing component code.
  • Application state — serialized into a <script type="qwik/json"> block, so any component can resume without its parent being present.

When the page loads, Qwik doesn’t re-run your components. Evidence for this claim Qwik resumability restores listeners and application state without rerunning component initialization on page startup. Scope: Qwik resumability model. Confidence: high · Verified: Qwik: Resumable As the docs put it, “Resumability is a way for a framework to recover its state without re-executing the application components on the client.” The result, in the docs’ words: “Qwik apps work instantly without any delay because they don’t need hydration, regardless of their size or complexity.”

This is not Islands Architecture (Astro). Islands still hydrate each island individually. Qwik hydrates nothing — there’s no per-island hydration cost because there’s no hydration at all.

What crawlers actually receive

Because Qwik pre-renders everything server-side and never re-renders on the client, the HTML 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. fetches already contains your complete content — text, links, headings, the lot — for a route that’s actually rendered on the server. RenderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. is a per-route decision in QwikCity (SSR, SSG, or a mix); a route left in client-only mode, or content fetched from a client-side effect instead of routeLoader$(), doesn’t get this benefit just because the project uses Qwik. Google processes JavaScript in two waves (HTML first, then a deferred render pass that can lag hours to days). A heavily client-rendered app risks content only showing up in that second wave. A properly server-rendered Qwik route sidesteps that risk: there’s no render-queue delay because the content was never waiting on client JS in the first place. Treat “Qwik pages behave like static HTML for 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.” as the architecture’s intent, then confirm it on your deployed URLs with view-source — the safest posture for JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. is verifying the response, not trusting the framework label.

Bing works the same way — a two-wave model that prefers server-rendered HTML for efficiency. Qwik’s HTML-first output benefits both engines for the same reason.

Core Web Vitals: a structural advantage

Google uses field dataPerformance metrics captured from real users, not lab tests. (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.) for the Page Experience signal, and INPInteraction to Next Paint — the input-to-paint latency at the 75th percentile of a page's interactions. ≤200 ms is good. replaced FID as a Core Web Vital in March 2024. Here’s where Qwik’s architecture pays off directly: with the Qwikloader as the only script running on load — the docs note sites “can boot with about 1kb of JS (regardless of application complexity)” — there’s almost nothing to block the main thread. Total Blocking Time trends toward zero and INP stays low because there’s no hydration pass competing with the user’s first interactions. A bigger Qwik app doesn’t mean a bigger startup bill; the JavaScript is fetched lazily, per interaction, not as one boot bundle.

That’s the difference from the established frameworks. Next.js, Nuxt, and Angular SSR all put content in the HTML too — they’re not bad for SEO — but they carry a hydration cost that scales with the app. Qwik removes that cost by design.

That’s the architectural expectation, not a measured result. It doesn’t guarantee a specific INP, 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., or 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. score for your site — third-party scripts, analytics tags, and per-route data-fetching cost can all eat into the advantage. Confirm it with field data (CrUX, Search ConsoleGoogle's free tool for monitoring crawling, indexing, and search performance.’s Core Web Vitals reportThe Google Search Console report (under Experience) that shows how your indexed URLs perform on the Core Web Vitals — LCP, INP, and CLS — using real-user field data from CrUX, grouped by device, status, and clusters of similar-performing URLs.) on your own deployed URLs before making a performance claim to a client or stakeholder; see the How to Measure tab for the specific KPIs and cadence.

Meta tags and the document head

In QwikCity, page metadata lives in a head export from each route file. For static metadata it’s a constant: Evidence for this claim Qwik City route modules can export a DocumentHead value or function for route metadata. Scope: Qwik City route metadata. Confidence: high · Verified: Qwik City: Head

export const head: DocumentHead = {
  title: 'Qwik SEO Guide',
  meta: [
    { name: 'description', content: 'How Qwik resumability helps SEO.' },
    { property: 'og:title', content: 'Qwik SEO Guide' },
    { property: 'og:description', content: 'Resumability, meta tags, sitemaps.' },
  ],
  links: [
    { rel: 'canonical', href: 'https://example.com/qwik-seo/' },
  ],
};

All of this is rendered server-side into <head>, so it’s in the HTML the crawler sees. The canonical pattern — links: [{ rel: 'canonical', href: '...' }] — is easy to miss but is the right place for it.

For dynamic metadata (blog posts, products), the SEO-critical pattern is routeLoader$(). It runs server-side before the HTML response, and the head export can be a function that receives the resolved loader value:

export const usePost = routeLoader$(async ({ params }) => {
  return await getPost(params.slug); // runs on the server
});

export const head: DocumentHead = ({ resolveValue }) => {
  const post = resolveValue(usePost);
  return {
    title: post.title,
    meta: [{ name: 'description', content: post.excerpt }],
    links: [{ rel: 'canonical', href: `https://example.com/blog/${post.slug}/` }],
  };
};

This is QwikCity’s equivalent of getServerSideProps. The discipline that matters for SEO: fetch metadata data with routeLoader$(), not in a client-side effect (useSignal()/useResource$() that run in the browser). If you set the title from a client effect, it won’t be in the first-wave HTML.

Structured data (JSON-LD)

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. goes in the head export’s scripts array, rendered inline in <head> server-side and fully crawlable:

export const head: DocumentHead = ({ resolveValue }) => {
  const post = resolveValue(usePost);
  return {
    title: post.title,
    scripts: [
      {
        props: { type: 'application/ld+json' },
        script: JSON.stringify({
          '@context': 'https://schema.org',
          '@type': 'Article',
          headline: post.title,
          datePublished: post.date,
        }),
      },
    ],
  };
};

Because it’s serialized into the HTML response, there’s no rendering delay before the markup is available to crawlers. One setup detail that’s easy to miss: the scripts field only produces output if your project’s router-head component actually renders head.scripts (typically via dangerouslySetInnerHTML) — it’s not automatic just because you populated the array. If a starter template’s router-head.tsx doesn’t already loop over head.scripts, add that before trusting the JSON-LD shows up in the response.

Sitemaps and robots.txt

  • SSG builds: the sitemapOutFile config option auto-generates sitemap.xml during the static build — but it only includes routes that were actually built. A route excluded from the static build (dynamic params you didn’t pre-render, a page gated behind a condition) won’t be in the 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. even though it exists.
  • SSR sites: there’s no automatic sitemap — add a route at src/routes/sitemap.xml/index.ts with a RequestHandler that builds and returns the XML dynamically.
  • robots.txt: drop it in /public/ so it’s served at the root.
  • Canonical and sitemap URLs both depend on origin. QwikCity builds absolute URLs from the configured origin/base (or a forwarded-header origin in SSR middleware). A wrong or leaked preview/staging origin in that configuration produces wrong canonicals and sitemap entries in production — check the actual output, not just the config file, after any adapter or deployment change.

International SEO

Two mechanisms, used together:

  • rewriteRoutes in QwikCity config maps localized URL paths to your routes without duplicating components (e.g. /it/documentazione//docs/). This gives you clean, localized URLs — the foundation for an international URL structureURL structure is how the parts of a web address — scheme, domain, path, query string, and fragment — are organized and formatted. It mostly affects crawling, usability, and how engines understand a page, not rankings directly..
  • Qwik Speak (a separate library) handles content translation.
  • 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. tags go in the head export’s links array, pointing each locale at its alternates. See 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. and 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. for the cross-engine rules.

SSR vs. SSG for SEO

Both put content in the HTML, so both are crawler-safe — and QwikCity lets you mix modes per route, not just once for the whole project. Choose per route by content shape:

  • SSG (static adapter) produces pure HTML files, no server needed — ideal for content that doesn’t change per request. Automatic sitemap (for routes that were actually built), cheapest hosting, 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. from a CDN. The tradeoff is freshness: content only updates on the next build, and a route that fails to build simply isn’t there.
  • SSR renders per request — needed for personalized or frequently changing data. Run it at the edge (Cloudflare, Vercel, Netlify) for low TTFB, and set Cache-Control headers on SSR responses so the CDN can serve repeats without re-rendering. The tradeoff is that a failure at request time (a slow or errored routeLoader$()) affects the live response, so add error handling for the data fetch rather than letting it surface as a broken page.

Neither mode is “more SEO-safe” than the other by default — pick per route based on how often the content changes and what happens if the data source is briefly unavailable, then verify the deployed response either way.

What to watch out for

  • Not everything serializes. Class instances, promises, and streams can’t be put into the HTML state. Code that touches them has to run client-only — keep it out of anything that needs to be in the server HTML (especially metadata). If you deliberately exclude a value with noSerialize(), remember it comes back as undefined after the app resumes on the client — don’t reach for it on anything the page still needs to read post-resume, including metadata paths.
  • useVisibleTask$() is a real escape hatch, use it sparingly. It runs eagerly on the client after initial render — the one hook that deliberately works against resumability. It doesn’t block rendering or hurt the initial HTML, but it does mean client JS is running that Qwik would otherwise have deferred. Fine for genuinely client-only work (a chart library, a map); wrong choice for anything that should be feeding server-rendered metadata.
  • Verify 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. are in the source, not injected by JS. View-source (not just DevTools’ rendered DOM) should show your title, canonical, and JSON-LD. If they only appear in the rendered DOM, you set them from a client effect instead of the head export / routeLoader$().
  • routeLoader$() vs. useResource$()/useSignal(). Loaders run on the server and feed the head; the others can run in the browser. For anything that must be in metadata or first-wave HTML, use the loader.
  • Smaller ecosystem. Qwik has far fewer CMSA content management system (CMS) is software that lets users create, manage, and publish digital content — like blog posts and pages — without writing raw code. WordPress, Drupal, and Joomla are the most common open-source CMS platforms. connectors and SEO plugins than Next.js, and a smaller community to draw on. The framework is capable; you’ll just write more glue yourself. builder.io’s own site runs on Qwik, so it’s used in production — but go in eyes-open about the maturity gap.

Where this sits

Qwik is one of two frameworks 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. story — the other being SolidJS. The key distinction to keep straight: Qwik resumes; SolidJS (and React, Vue, Angular) hydrate. Both still output content-rich HTML via their meta-frameworks, so 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. is fine either way; the resumability angle is what makes Qwik’s INP/TBT profile structurally the best of the group. For the metric side, see 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.; for the foundations, the 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.