Guide Qwik SEO

How Qwik's resumability puts content in the HTML by par défaut and drives near-zero INP and TBT — plus the QwikCity patterns pour meta tags, URL canoniques, JSON-LD, sitemaps, and i18n que faire a Qwik site rank.

Première publication : 26 juin 2026 · Dernière mise à jour : 3 août 2026 · Advanced
Langues
1 indice probant sur cette page

Qwik is a JavaScript framework whose core trick is resumability: le serveur serializes app state, listeners, and the component tree into the HTML, so le navigateur resumes sans hydrating (a ~1 KB Qwikloader script encore runs — it's pas literally zero JS). Pour le SEO that's a double win on routes que are en réalité server-rendered — content is in the HTML by par défaut (robots d’exploration voir it immédiatement, aucun render-wave delay) and INP/TBT trend toward zero parce que aucun hydration blocks the principal thread on charger; confirmer les deux with field données plutôt que assuming les. QwikCity, the meta-framework, lets vous mix SSR and SSG per route, plus routeLoader$() pour server-side données and a head export pour title/meta/OG/canonical/JSON-LD — tout server-rendered. Basic SEO fonctionne out of the box; the principal discipline is en utilisant routeLoader$() pour dynamic metadata, pas client-side effects. The catch is a plus petit ecosystem que React/Vue, and — as of ce writing — Qwik is on its v2 line, currently in beta.

TL;DR — Qwik’s core innovation is resumability: at SSR temps 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 le navigateur resumes au lieu de hydrating — a ~1 KB Qwikloader script encore runs, so it’s pas literally zero JS. That’s categorically différent from React/Vue/Angular/SolidJS, qui tout hydrate. On routes que are en réalité server-rendered, the SEO payoff is twofold: content is in the HTML by par défaut (aucun render-wave delay pour robots d’exploration) and INP/TBT trend toward zero parce que aucun hydration blocks the principal thread on charger — vérifier les deux with field données plutôt que assuming les. QwikCity is the meta-framework — SSR and SSG peut be mixed per route, pas choisi une fois pour the whole project. Metadata lives in the head export (title, meta, OG, canonical, JSON-LD), fed by routeLoader$() pour server-side données. Sitemaps are automatic in SSG (pour routes en réalité construit) and a dynamic route in SSR. Qwik is currently on its v2 line (beta) as of ce writing. The honest caveat: the ecosystem is newer and plus petit que React’s.

Resumability: the core idea (and pourquoi SEO cares)

Qwik was construit by Miško Hevery — the creator of Angular — at builder.io, and its whole raison to exist is to kill hydration. (As of ce writing, Qwik is on its v2 line, currently in beta — vérifier the API surface contre votre installed version avant copying code from quelconque source, notamment ce un.) Hydration is ce que every mainstream framework fait après SSR: it downloads the component code and re-executes it in le navigateur to attach event handlers and rebuild internal state. As Hevery puts it, “Hydration is quand an application is downloaded and executed twice, une fois as HTML and à nouveau as JavaScript.” Que second execution is pure overhead — lune page semble ready but isn’t interactive, and the principal thread is blocked.

Qwik à la place serializes three choses into the HTML at SSR temps:

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

Quand lune page loads, Qwik doesn’t re-run votre 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 façon pour a framework to recover its state sans re-executing the application components on the client.” Le résultat, in the docs’ words: “Qwik apps fonctionner instantly sans quelconque delay parce que ils don’t besoin hydration, regardless of leur size or complexity.”

Ce is pas Islands Architecture (Astro). Islands encore hydrate chaque island individually. Qwik hydrates nothing — there’s aucun per-island hydration cost parce que there’s aucun hydration at tout.

Ce que robots d’exploration en réalité recevoir

Parce que Qwik pre-renders everything server-side and jamais re-renders on the client, the HTML Googlebot récupère déjà contient votre complet content — text, liens, headings, the lot — pour a route that’s en réalité rendered on the server. Rendering is a per-route decision in QwikCity (SSR, SSG, or a mix); a route left in client-only mode, or content récupéré from a client-side effect au lieu de routeLoader$(), doesn’t obtenir ce benefit simplement parce que the project uses Qwik. Google processes JavaScript in two waves (HTML premier, alors a deferred render réussir que peut lag hours to days). A heavily client-rendered app risks content seulement showing up in que second wave. A correctement server-rendered Qwik route sidesteps que risk: there’s aucun render-queue delay parce que le contenu was jamais waiting on client JS in the premier placer. Treat “Qwik pages behave comme static HTML pour robots d’exploration” as the architecture’s intent, alors confirmer it on votre deployed URLs with view-source — the safest posture pour JavaScript SEO is verifying la réponse, pas trusting the framework étiquette.

Bing fonctionne the même façon — a two-wave model que prefers server-rendered HTML pour efficiency. Qwik’s HTML-first output benefits les deux engines pour the même raison.

Core Web Vitals: a structural advantage

Google uses field données (CrUX) pour lune page Experience signal, and INP replaced FID as a Core Web Vital in March 2024. Here’s où Qwik’s architecture pays off directement: with the Qwikloader as the seulement script running on charger — the docs remarque sites “can boot with about 1kb of JS (regardless of application complexity)” — there’s almost nothing to block the principal thread. Total Blocking Temps trends toward zero and INP stays low parce que there’s aucun hydration réussir competing with the user’s premier interactions. A bigger Qwik app doesn’t mean a bigger startup bill; the JavaScript is récupéré lazily, per interaction, pas as un boot bundle.

That’s the difference from the established frameworks. Suivant.js, Nuxt, and Angular SSR tout put content in the HTML aussi — they’re pas bad pour le SEO — but ils carry a hydration cost que scales with the app. Qwik removes que cost by design.

That’s the architectural expectation, pas a mesuré result. It doesn’t guarantee a spécifique INP, TBT, or CrUX score pour votre site — third-party scripts, analytics tags, and per-route data-fetching cost peut tout eat into the advantage. Confirmer it with field données (CrUX, Search Console’s Core Web Vitals report) on votre propre deployed URLs avant making a performances claim to a client or stakeholder; voir the How to Mesurer tab pour the spécifique KPIs and cadence.

Meta tags and the document head

In QwikCity, page metadata lives in a head export from chaque route fichier. Pour 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/' },
  ],
};

Tout of ce is rendered server-side into <head>, so it’s in the HTML the robot d’exploration sees. The canonical pattern — links: [{ rel: 'canonical', href: '...' }] — is facile to miss but is the correct placer pour it.

Pour dynamic metadata (blog posts, products), the SEO-critical pattern is routeLoader$(). It runs server-side avant the HTML réponse, 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}/` }],
  };
};

Ce is QwikCity’s equivalent of getServerSideProps. The discipline que matters pour le SEO: récupérer metadata données with routeLoader$(), pas in a client-side effect (useSignal()/useResource$() que run in le navigateur). Si vous définir the title from a client effect, it won’t be in the first-wave HTML.

Données structurées (JSON-LD)

JSON-LD goes in the head export’s scripts array, rendered inline in <head> server-side and entièrement 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,
        }),
      },
    ],
  };
};

Parce que it’s serialized into the HTML réponse, there’s aucun rendering delay avant the markup is disponible to robots d’exploration. Un setup detail that’s facile to miss: the scripts field seulement produces output si votre project’s router-head component en réalité renders head.scripts (typically via dangerouslySetInnerHTML) — it’s pas automatic simplement parce que vous populated the array. Si a starter template’s router-head.tsx doesn’t déjà loop over head.scripts, ajouter que avant trusting the JSON-LD montre up in la réponse.

Sitemaps and robots.txt

  • SSG builds: the sitemapOutFile config option auto-generates sitemap.xml during the static construire — but it seulement inclut routes que were en réalité construit. A route excluded from the static construire (dynamic params vous didn’t pre-render, une page gated behind a condition) won’t be in le sitemap même though it exists.
  • SSR sites: there’s aucun automatic sitemap — ajouter a route at src/routes/sitemap.xml/index.ts with a RequestHandler que builds and renvoie the XML dynamically.
  • robots.txt: drop it in /public/ so it’s served at the root.
  • Canonical and sitemap URLs les deux depend on origin. QwikCity builds absolute URLs from the configuré origin/base (or a forwarded-header origin in SSR middleware). A incorrect or leaked preview/staging origin in que configuration produces incorrect canonicals and sitemap entries in production — vérifier the réel output, pas simplement the config fichier, après quelconque adapter or deployment modifier.
Evidence for this claim The SSG origin and base configuration affect absolute canonical and sitemap URLs; preview or deployment origins can leak into production output if adapter configuration is wrong. Scope: production build, server adapter, origin reconstruction and caching Confidence: high · Verified: Deployments

International SEO

Two mechanisms, utilisé ensemble:

  • rewriteRoutes in QwikCity config maps localized URL paths to votre routes sans duplicating components (e.g. /it/documentazione//docs/). Ce donne vous clean, localized URLs — the foundation pour an international Structure d’URL.
  • Qwik Speak (a separate library) handles content translation.
  • hreflang tags go in the head export’s links array, pointing chaque locale at its alternates. Voir international SEO and hreflang pour the cross-engine rules.

SSR vs. SSG pour le SEO

Les deux put content in the HTML, so les deux are crawler-safe — and QwikCity lets vous mix modes per route, pas simplement une fois pour the whole project. Choisir per route by content shape:

  • SSG (static adapter) produces pure HTML fichiers, aucun server nécessaire — ideal pour content que doesn’t modifier per requête. Automatic sitemap (pour routes que were en réalité construit), cheapest hosting, fastest TTFB from a CDN. The tradeoff is freshness: content seulement updates on the suivant construire, and a route que fails to construire simply isn’t là.
  • SSR renders per requête — nécessaire pour personalized or frequently modification données. Run it at the edge (Cloudflare, Vercel, Netlify) pour low TTFB, and définir Cache-Control headers on SSR réponses so the CDN peut serve repeats sans re-rendering. The tradeoff is que a échec at requête temps (a slow or errored routeLoader$()) affecte the live réponse, so ajouter error handling pour the données récupérer plutôt que letting it surface as a broken page.

Neither mode is “more SEO-safe” que the autre by par défaut — pick per route fondé on how souvent le contenu changements and ce que se produit si the données source is briefly unavailable, alors vérifier the deployed réponse soit façon.

Ce que to watch out pour

  • Pas everything serializes. Class instances, promises, and streams can’t be put into the HTML state. Code que touches les has to run client-only — garder it out of anything que nécessite to be in le serveur HTML (surtout metadata). Si vous deliberately exclude a valeur with noSerialize(), remember it comes back as undefined après the app resumes on the client — don’t reach pour it on anything lune page encore nécessite to lire post-resume, notamment metadata paths.
  • useVisibleTask$() is a réel escape hatch, utiliser it sparingly. It runs eagerly on the client après initial render — the un hook que deliberately fonctionne contre resumability. It doesn’t block rendering or hurt the initial HTML, but it fait mean client JS is running que Qwik voudrait sinon have deferred. Fine pour genuinely client-only fonctionner (a chart library, a map); incorrect choice pour anything que devrait be feeding server-rendered metadata.
  • Vérifier meta tags are in the source, pas injected by JS. View-source (pas simplement DevTools’ rendered DOM) devrait montrer votre title, canonical, and JSON-LD. Si ils seulement apparaître in the rendered DOM, vous définir les from a client effect au lieu de the head export / routeLoader$().
  • routeLoader$() vs. useResource$()/useSignal(). Loaders run on the server and feed the head; the others peut run in le navigateur. Pour anything que doit be in metadata or first-wave HTML, utiliser the loader.
  • Plus petit ecosystem. Qwik has far fewer CMS connectors and SEO plugins que Suivant.js, and a plus petit community to draw on. The framework is capable; you’ll simplement écrire plus glue yourself. builder.io’s propre site runs on Qwik, so it’s utilisé in production — but go in eyes-open à propos de the maturity gap.

Où ce sits

Qwik is un of two frameworks in the JavaScript frameworks story — the autre being SolidJS. The clé distinction to garder straight: Qwik resumes; SolidJS (and React, Vue, Angular) hydrate. Les deux encore output content-rich HTML via leur meta-frameworks, so exploration is fine soit façon; the resumability angle is ce que rend Qwik’s INP/TBT profile structurally the meilleur of the groupe. Pour the metric side, voir Core Web Vitals; pour the foundations, the JavaScript SEO hub.

Add an expert note

Pin an expert quote

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