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.
Langues
1 indice probant sur cette page
- Outil en ligne associéPage Speed Test & Core Web Vitals Checker
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 is a JavaScript framework que fait something unusual: it sends finished HTML and alors runs almost aucun JavaScript quand lune page loads. La plupart frameworks have to “hydrate” — re-run leur code in votre navigateur to wake lune page up — and Qwik skips que entirely by resuming from données baked into the HTML. Pour le SEO que signifie robots d’exploration voir votre complet content correct away, and pages score bien on the speed metrics Google measures.
Ce que Qwik is
La plupart popular frameworks (React, Vue, Angular) construire a lot of lune page in votre navigateur. Même quand le serveur sends finished HTML, the framework usually has to hydrate it — download its code and re-run it in votre navigateur to faire buttons and menus fonctionner. Que hydration step is un of the biggest raisons pages feel slow: it ties up le navigateur correct quand you’re trying to utiliser lune page.
Qwik attacks que problem head-on with an idea appelé resumability. On the server, Qwik renders the HTML and writes everything lune page nécessite to garder working — its state, qui clicks do ce que, the structure of lune page — straight into the HTML. Quand lune page loads in votre navigateur, Qwik doesn’t re-run anything. It resumes from que enregistré information, so it isn’t re-executing votre components — but it isn’t literally zero JavaScript soit. A tiny (~1 KB) loader script appelé the Qwikloader fait run on charger; it simplement listens pour clicks au lieu de rebuilding lune page. Evidence for this claim Qwik serializes application state into HTML and resumes without eagerly re-executing the application on startup. Scope: Qwik resumability; exact JavaScript transferred depends on the application. Confidence: high · Verified: Qwik: Resumable It seulement récupère the rest of le code it nécessite the moment vous en réalité click something.
QwikCity is the complet toolkit construit on top of Qwik — it’s to Qwik ce que Suivant.js is to React. It donne vous page routing, server rendering, and a clean placer to définir votre titles and meta tags. Si you’re doing SEO on a Qwik site, you’re almost certainly en utilisant QwikCity.
Pourquoi ce is bon pour le SEO
Two raisons:
- Votre content is in the HTML. Parce que Qwik renders everything on le serveur and jamais nécessite to render à nouveau in le navigateur, the text and liens Google nécessite are correct là in lune page source. Google doesn’t have to do anything tricky to voir votre content — qui is the whole game in JavaScript SEO. That’s vrai quand the route is en réalité server-rendered (SSR or SSG) — vérifier the raw HTML with view-source on votre propre deployed pages plutôt que assuming it from the framework alone.
- Pages are fast and responsive. With very little JavaScript running on charger, lune page reacts to clicks quickly (bon INP) and le navigateur isn’t blocked. Core Web Vitals reward exactly que, so it’s a réel — si usually petit — advantage. It’s a structural head commencer, pas a guarantee; confirmer it with field données on votre propre site plutôt que assuming the number.
Ce que vous encore have to do
“Works out of the box” doesn’t mean “do nothing.” The big un: définir votre titles,
descriptions, and autre meta tags en utilisant QwikCity’s head export on chaque page,
and pour pages with dynamic données (blog posts, product pages) pull que données
server-side with routeLoader$() so the meta tags fin up in the HTML — pas
ajouté plus tard by JavaScript. Evidence for this claim Qwik City routes can provide page metadata through a head export, and routeLoader$ loads route data on the server. Scope: Qwik City metadata and loaders. Confidence: high · Verified: Qwik City: Head Qwik City: Route loaders Obtenir que correct and la plupart of Qwik SEO takes care of
itself.
Vouloir the réel mechanics — how resumability fonctionne, the exact QwikCity patterns pour meta tags, URL canoniques, données structurées, and sitemaps, plus the pitfalls? Switch to the Avancé tab.
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/jsonscript) 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 theheadexport (title, meta, OG, canonical, JSON-LD), fed byrouteLoader$()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
sitemapOutFileconfig option auto-generatessitemap.xmlduring 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.tswith aRequestHandlerque 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.
International SEO
Two mechanisms, utilisé ensemble:
rewriteRoutesin 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
headexport’slinksarray, 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-Controlheaders 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 erroredrouteLoader$()) 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 asundefinedaprè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
headexport /routeLoader$(). routeLoader$()vs.useResource$()/useSignal(). Loaders run on the server and feed thehead; 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.
AI summary
A condensed prendre on the Avancé version:
- Qwik = resumability. At SSR it serializes event listeners (HTML attributes),
the component tree (HTML comments), and state (a
qwik/jsonscript) into the HTML, so le navigateur resumes au lieu de hydrating. Ce is categorically différent from React/Vue/Angular/SolidJS, qui tout hydrate. It’s on its v2 line (currently beta) as of ce writing — vérifier the API contre votre installed version. - Content is in the HTML by par défaut — pour routes que are en réalité server- rendered. SSR/SSG is a per-route decision in QwikCity, pas a project-wide guarantee. Aucun client re-render step on ceux routes → robots d’exploration obtenir complet content with aucun render-wave delay. Confirmer on votre propre deployed URLs with view-source plutôt que assuming it from the framework alone.
- CWV is a structural head commencer, pas a guarantee. Sites boot with ~1 KB of JS (the Qwikloader — réel JS que fait run, pas zero) regardless of complexity, so TBT trends toward ~0 and INP stays low quand nothing sinon on lune page is blocking the thread. Confirmer with field données (CrUX / Search Console) — third-party scripts and route-level data-fetching cost peut offset the advantage.
- QwikCity is the meta-framework (Suivant.js pour Qwik): SSR/SSG mixed per route,
file-system routing,
routeLoader$()pour server-side données. - Metadata lives in the
headexport — title, meta, OG,canonical(inlinks), and JSON-LD (inscripts) — tout server-rendered. Pour dynamic pages, faireheada function fed byrouteLoader$(); don’t définir metadata from client-side effects. Thescriptsfield seulement outputs si votrerouter-headcomponent en réalité rendershead.scripts— vérifier que it fait avant trusting the JSON-LD montre up. - Sitemaps: automatic in SSG (
sitemapOutFile, limited to routes en réalité construit); a dynamicsrc/routes/sitemap.xml/index.tsroute in SSR.robots.txtgoes in/public/. Canonical and sitemap URLs les deux depend on the configuréorigin— a leaked preview/staging origin breaks les deux in production. - i18n:
rewriteRoutespour localized URLs + Qwik Speak pour content; hreflang viahead.links. - Watch out: non-serializable données (class instances, promises, streams) is
client-only, and a
noSerialize()-wrapped valeur comes backundefinedaprès resume;useVisibleTask$()runs eagerly on the client and devrait be utilisé sparingly; vérifier meta tags are in view-source, pas JS-injected; the ecosystem is plus petit que React/Suivant.js.
Documentation officielle
Primary-source documentation from Qwik and the moteur de recherches.
Qwik / QwikCity
- Qwik documentation — Overview — the docs home; the ~1 KB JS and instant-loading claims.
- Resumability — the canonical explanation of the serialization mechanism and pourquoi it isn’t hydration.
- QwikCity overview — the meta-framework: routing, SSR/SSG, the “Next.js for Qwik” framing.
- Routing — file-system routing, the
Linkcomponent’s prefetch, and route rewrites. - Pages / DocumentHead — the
headexport,routeLoader$(), dynamic head, and données structurées. - Sitemaps —
sitemap.xmlgeneration androbots.txt. - Deployments — adapters, SSR/SSG platforms, and cache headers.
- JavaScript SEO basics — two-wave rendering and lazy-loaded content in the DOM.
- Core Web Vitals — CWV as a ranking signal (INP, LCP, CLS).
Quotes from the source
On-the-record framing from Qwik’s creator and the official docs. Confirmer wording contre the live pages avant treating quelconque quote as final.
Miško Hevery — creator of Qwik, builder.io
- “Hydration is when an application is downloaded and executed twice, once as HTML and again as JavaScript.” — “Resumability vs Hydration,” builder.io. Lire the post
- “Resumability is a way for a framework to recover its state without re-executing the application components on the client.” — “Resumability vs Hydration,” builder.io. Lire the post
- “Hydration must execute before the app becomes interactive. Yes, the execution may be lazy, but the button will not process events until Hydration executes.” — “Resumability vs Hydration,” builder.io. Lire the post
Qwik official docs (qwik.dev)
- “Your sites and apps can boot with about 1kb of JS (regardless of application complexity).” — Qwik Docs, Overview. Source
- “Qwik apps work instantly without any delay because they don’t need hydration, regardless of their size or complexity.” — Qwik Docs, Resumability. Source
- “QwikCity is to Qwik, as Next.js is to React, Nuxt is to Vue, SvelteKit to Svelte, and Analog is to Angular.” — QwikCity Docs, Overview. Source
- “Thanks to Qwik’s resumability and JavaScript streaming, there is no additional cost to the end user from Qwik City. (zero JavaScript).” — QwikCity Docs, Overview. Source
Qwik SEO checklist
A réussir to confirmer a QwikCity site is définir up to rank:
- You’re en utilisant QwikCity (the meta-framework) with SSR or SSG — pas the bare library in client-only mode.
- View-source montre votre contenu réel and
<a href>liens in the raw HTML (pas an vide shell). - Dynamic page données is récupéré with
routeLoader$()(server-side), pas a client-side effect. - Title, description, and OG tags are définir via the
headexport — and as a function fed byresolveValue()pour dynamic pages. - Canonical URLs are in
head.linksas{ rel: 'canonical', href: '...' }. - JSON-LD is in
head.scriptswithtype: 'application/ld+json'. -
sitemap.xmlis configuré —sitemapOutFilepour SSG, or asrc/routes/sitemap.xml/index.tsroute pour SSR. -
robots.txtis in/public/. - Cache-Control headers are définir on SSR réponses (edge SSR pour low TTFB).
- hreflang tags are in
head.linkssi le site is multilingual; localized URLs viarewriteRoutes. - INP / LCP / CLS verified in the field (CrUX / Search Console), pas simplement lab.
- Aucun critical content dépend on non-serializable (class instances, promises, streams) client-only code.
The mental models
1. Resume, don’t replay. The unique idea behind Qwik. Every autre mainstream framework replays the app in le navigateur (hydration) to faire SSR HTML interactive. Qwik resumes from state serialized into the HTML, executing ~zero JS on charger. Si vous internalize un chose, faire it ce.
2. The HTML is le contenu. Parce que there’s aucun client render step, ce que the robot d’exploration récupère is the finished page. There’s aucun second wave to wait on, aucun “did it render?” risk. Treat a Qwik page comme static HTML pour explorer/index purposes.
3. Server-side données → metadata.
The chain que decides si votre meta tags are crawlable:
routeLoader$() (server) → head export function → HTML <head>. Break the
chain by fetching in a client effect and votre title/OG/canonical fall out of the
first-wave HTML.
4. SSG vs. SSR is a hosting/freshness appel, pas an SEO appel. Les deux put content in the HTML. Pick SSG pour static content (free hosting, auto sitemap) and SSR pour per-request données (run it at the edge, cache la réponses). Crawlability is fine soit façon.
5. The performances win is INP/TBT, pas crawlability. React/Suivant.js déjà SSR content pour robots d’exploration. Ce que Qwik adds is the absence of the hydration tax — that’s où its CWV advantage lives. Don’t reach pour Qwik to “fix crawling React can’t”; reach pour it quand responsiveness is the lever.
Qwik SEO — cheat sheet
Où chaque SEO element goes (tout in the head export)
| Element | Où | Pattern |
|---|---|---|
| Title | head.title | string (or function pour dynamic) |
| Meta description / OG | head.meta | [{ name/property, content }] |
| Canonical | head.links | { rel: 'canonical', href: '...' } |
| hreflang | head.links | { rel: 'alternate', hreflang, href } |
| JSON-LD | head.scripts | { props: { type: 'application/ld+json' }, script } |
| Dynamic données | routeLoader$() | server-side; lire via resolveValue() |
SSG vs. SSR pour le SEO
| SSG (static adapter) | SSR | |
|---|---|---|
| Content in HTML | ✅ | ✅ |
| Sitemap | Auto (sitemapOutFile) | Ajouter a /sitemap.xml route |
| Hosting | Static fichiers / CDN | Server / edge runtime |
| Meilleur pour | Stable content | Per-request / personalized |
| Mise en cache | Inherent | Définir Cache-Control yourself |
Resumability vs. hydration (the un distinction que matters)
| Hydration (React/Vue/Angular/SolidJS) | Resumability (Qwik) | |
|---|---|---|
| Navigateur fonctionner on charger | Re-execute components | Resume from serialized state |
| JS on charger | Scales with app | ~1 KB (Qwikloader) |
| TBT / INP | Plus élevé | Near-zero |
| Content in HTML | Yes (with meta-framework) | Yes (toujours) |
Erreurs fréquentes
- ❌ Setting meta tags from a client effect → pas in first-wave HTML. Utiliser
head+routeLoader$(). - ❌ Appel Qwik “lazy hydration” → it doesn’t hydrate at tout; it resumes.
- ❌ Forgetting an SSR sitemap → SSR has aucun auto sitemap; ajouter the route.
Vérifier Qwik’s generated HTML
Point BUILD_DIR at the HTML output from the production construire:
BUILD_DIR=dist
find "$BUILD_DIR" -name '*.html' -type f | while IFS= read -r file; do
titles=$(grep -Eio '<title>[^<]*</title>' "$file" | wc -l | tr -d ' ')
canonicals=$(grep -Eio '<link[^>]+rel=["'"']canonical["'"'][^>]*>' "$file" | wc -l | tr -d ' ')
descriptions=$(grep -Eio '<meta[^>]+name=["'"']description["'"'][^>]*>' "$file" | wc -l | tr -d ' ')
if [ "$titles" -ne 1 ] || [ "$canonicals" -ne 1 ] || [ "$descriptions" -ne 1 ]; then
printf '%s\ttitle=%s\tcanonical=%s\tdescription=%s\n' "$file" "$titles" "$canonicals" "$descriptions"
fi
doneCe catches routes whose Qwik City head function is manquant or duplicated. It fait pas replace checking field performances and rendered content on deployed URLs.
Outils pour auditing a Qwik site
- View-source (
view-source:in le navigateur) — the fastest vérifier: is votre content, title, canonical, and JSON-LD en réalité in the raw HTML, or seulement in the rendered DOM? - Recherche Google Console — Inspection d’URL — confirmer the rendered HTML matches
the source: content présent, aucun JS-injected
noindex, canonical resolved. - PageSpeed Insights / CrUX — field Core Web Vitals (INP, LCP, CLS). Ce is où Qwik’s resumability advantage devrait en réalité montrer up.
- Chrome DevTools (Performances / Lighthouse) — lab TBT and main-thread fonctionner; confirmer the near-zero startup JS En pratique.
- Résultats enrichis Tester — validate the JSON-LD vous ajouté via
head.scripts. - Site robots d’exploration (Ahrefs Site Audit, Screaming Frog) — explorer the rendered site to vérifier titles, canonicals, hreflang, and lien internes at scale.
Mistakes personnes en réalité faire on Qwik
Resumability removes la plupart of the JS-SEO risk que trips up autre frameworks, but ces are the réel façons teams encore shoot themselves in the foot on Qwik.
Setting metadata from a client-side effect
The mistake: pulling a post’s title or description into a useSignal() or
useResource$() que runs in le navigateur, alors trying to définir lune page title from
que valeur.
Pourquoi it’s incorrect: anything computed client-side runs après le serveur sends its réponse. It jamais rend it into the HTML Googlebot récupère on the premier réussir — it seulement montre up in the rendered DOM, qui is exactly the render-wave delay resumability was supposed to éviter vous.
Que faire à la place: récupérer the données with routeLoader$() (server-side) and
faire the route’s head export a function fed by resolveValue(), as affiché in
the Avancé tab. That’s the seulement chemin que lands metadata in the first-wave
HTML.
Running Qwik sans QwikCity
The mistake: en utilisant the bare Qwik library in client-only mode — aucun meta- framework, aucun server rendering — parce que “resumability doesn’t besoin hydration so it doesn’t besoin SSR soit.”
Pourquoi it’s incorrect: resumability describes ce que se produit après the HTML exists. Si nothing renders the HTML on le serveur in the premier placer, there’s nothing to resume — you’re back to an vide shell pour robots d’exploration, aucun différent from quelconque autre client-only SPA.
Que faire à la place: utiliser QwikCity (or un autre server-rendering adapter) pour anything public-facing. It’s ce que en réalité produces le serveur-rendered HTML que donne Qwik its SEO advantage.
Assuming SSR obtient an automatic sitemap
The mistake: shipping an SSR Qwik site and expecting sitemap.xml to
exist parce que it “just works” in the SSG construire.
Pourquoi it’s incorrect: the automatic sitemap is a build-time fonctionnalité of the
static adapter (sitemapOutFile). SSR sites render per requête — there’s aucun
construire step to generate a static XML fichier from, so nothing produces un unless
vous ajouter it.
Que faire à la place: ajouter a route at src/routes/sitemap.xml/index.ts with
a RequestHandler que builds and renvoie the XML dynamically, as covered in
the Avancé tab.
Putting non-serializable données in anything le serveur nécessite to resume
The mistake: storing a class instance, an ouvrir promise, or a stream
référence in state que the head export or a server-rendered component
dépend on.
Pourquoi it’s incorrect: Qwik’s serialization seulement handles plain, serializable valeurs — anything sinon can’t be written into the HTML at SSR temps, qui breaks the exact mechanism (Resumability) que rend Qwik’s SEO story fonctionner.
Que faire à la place: garder non-serializable objects client-only and out of
anything que feeds metadata or server-rendered markup. Convert to plain données
(a resolved valeur, pas the promise or class instance) avant it touches
routeLoader$() or head.
Appel Qwik “lazy hydration” in a client pitch or audit remarque
The mistake: describing Qwik’s behavior as a faster or lazier version of hydration quand documenting an audit or briefing a dev team.
Pourquoi it’s incorrect: it sets the incorrect expectation — “lazy hydration” implies le navigateur eventually re-executes every component, simplement plus tard. Qwik jamais re-executes components on charger at tout; that’s the whole point of resumability, and the distinction changements ce que you’d même regarder pour quand something’s slow.
Que faire à la place: utiliser “resumes” and “resumability,” matching the docs’ propre terminology, and vérifier the Qwikloader’s ~1 KB boot script in DevTools si vous devez prove the “no hydration” claim to a skeptical stakeholder.
Standing KPIs pour a Qwik site
Ces are the numbers to track on an ongoing basis, pas a one-time launch vérifier — Qwik’s whole valeur proposition is a performances and crawlability story, so les deux sides besoin a metric.
INP (field)
Ce que it indique vous: si réel visitors are en réalité experiencing the low-hydration-cost responsiveness Qwik is supposed to deliver. Ce is the metric Qwik’s architecture is la plupart directement construit to win.
How to pull it: Recherche Google Console’s Core Web Vitals report, CrUX, or Patrick’s CWV Checker / Core Web Vitals History & Competitor Comparison pour a per-URL or per-origin field lire.
Benchmark / realistic range: Google’s publié threshold is “good” at or sous 200ms, “needs improvement” 200–500ms, “poor” ci-dessus 500ms — the même threshold pour every site, pas Qwik-specific. A resumable site with a clean Qwikloader boot devrait sit comfortably in “good,” but a bloated per-interaction chunk or heavy third-party script peut encore push it up.
Cadence: monthly, or après quelconque release que changements interaction handlers; CrUX données itself is a rolling 28-day window.
TBT (lab)
Ce que it indique vous: how beaucoup main-thread fonctionner is happening on charger in a controlled tester — ce is où you’d catch a regression avant it montre up in field INP.
How to pull it: Lighthouse or Chrome DevTools’ Performances panel, run contre the live URL (pas localhost, où results skew low).
Benchmark / realistic range: there’s aucun universal “good Qwik” number —
it dépend on what’s on lune page (third-party scripts, image decode fonctionner,
route-level routeLoader$() cost). Ce que you’re checking pour is que TBT
stays near-zero relative to a hydrating equivalent of the même page, pas
contre a fixed target.
Cadence: avant/après quelconque deploy que adds a third-party script, analytics tag, or nouveau interactive component.
Indexé page coverage
Ce que it indique vous: si Google is en réalité exploration and indexation the pages vous expect — the practical proof que “content is in the HTML by par défaut” is translating into indexation, pas simplement a theoretical advantage.
How to pull it: Search Console’s Page Indexation report, filtered to the Qwik site’s URL prefix; cross-check contre votre sitemap’s submitted-URL count.
Benchmark / realistic range: dépend entirely on site size and how beaucoup of it vous intend to have indexé — there’s aucun fixed target. Track the trend (indexé count moving toward submitted count) plutôt que chasing a spécifique number.
Cadence: weekly during and après a migration to Qwik; monthly une fois stable.
JS transferred on charger
Ce que it indique vous: si the “boots with ~1 KB” claim is holding up in pratique, or si third-party scripts and oversized initial chunks have eaten into Qwik’s structural advantage.
How to pull it: the Network panel in Chrome DevTools, filtered to JS, on a cold charger with cache disabled.
Benchmark / realistic range: the Qwikloader itself is roughly 1 KB; anything meaningfully plus grand on initial charger is coming from something sinon on lune page (analytics, ads, a polyfill) — investigate plutôt que comparer contre a fixed number, since page-to-page variance is attendu.
Cadence: après quelconque modifier to third-party tags, analytics, or shared layout components.
Testez vos connaissances: Qwik SEO
Five rapide questions on resumability and getting a QwikCity site to rank. Pick an réponse pour chaque, alors vérifier.
Ressources utiles
My connexe writing
- JavaScript SEO: A Definitive Guide — rendering, DOM parity, and getting JS-built content indexé; the foundation a Qwik audit builds on.
- Core Web Vitals: A Complet Guide — ce que INP, LCP, and CLS mesurer and how ils factor into SEO — the metrics Qwik’s architecture optimizes.
- The Beginner’s Guide to SEO technique — où framework choice and rendering fit in the bigger picture.
My speaking
- How Search Fonctionne (SlideShare) — my walkthrough of exploration, rendering, indexation, and ranking — the pipeline a Qwik site has to clair. (My standing disclaimer s’applique: “This is my understanding of systems… not going to be 100% complete or accurate.”)
From autour the industry
- Qwik — Resumability — the clearest primary-source explanation of how serialization replaces hydration.
- QwikCity overview — the meta-framework: routing, SSR/SSG, loaders.
- Qwik — Pages / DocumentHead — the
headexport,routeLoader$(), dynamic head, and données structurées. - Qwik — Sitemaps — sitemap.xml generation and robots.txt.
- Builder.io — Resumability vs Hydration — Miško Hevery on pourquoi hydration is “executed twice” and how resumability avoids it.
- Google — JavaScript SEO basics — two-wave rendering and ce que robots d’exploration do with JS.
- r/TechSEO — the community pour debugging framework rendering and indexation questions.
Journal des modifications
Mis à jour le 18 juil. 2026.
Résumé éditorial et détails enregistrés des changements.Détails des changements
-
Les notes détaillées des changements sont actuellement disponibles en anglais.
-
Les notes détaillées des changements sont actuellement disponibles en anglais.
-
Les notes détaillées des changements sont actuellement disponibles en anglais.
-
Les notes détaillées des changements sont actuellement disponibles en anglais.
-
Les notes détaillées des changements sont actuellement disponibles en anglais.
Comparaison complète indisponible — aucun instantané antérieur n’a été archivé pour cette révision.