Guide Svelte SEO

Svelte alone is client-side rendered — content isn't in the raw HTML. SvelteKit fixes que with SSR by par défaut. Rendering modes, svelte:head, charger functions, adapters, sitemaps, and the SPA-mode trap.

Première publication : 26 juin 2026 · Dernière mise à jour : 3 août 2026 · Advanced
Langues

Svelte SEO hinges on un distinction: plain Svelte is client-side rendered (a blank HTML shell), pendant que SvelteKit renders on le serveur by par défaut and ships complet HTML. Utiliser SvelteKit pour anything que nécessite to rank. Manage metadata with svelte:head, récupérer SEO données in +page.server.js charger functions, pick the correct adapter, and jamais ship SPA fallback mode (ssr: faux) — the SvelteKit docs themselves warn it has grand negative SEO impacts.

TL;DR — Svelte SEO is really a SvelteKit conversation. Bare Svelte is CSR-only — content isn’t in the raw HTML. SvelteKit defaults to SSR, supports prerendering (SSG) per route, and lets vous mix modes. Manage metadata with <svelte:head> and feed it from +page.server.js charger functions so it lands in the initial HTML. The sharpest trap: adapter-static with ssr: false fait pas prerender — it produces vide shells; SSR doit stay on during the construire. SvelteKit’s propre docs warn SPA fallback has “grand negative performances and SEO impacts.” Utiliser History API routing (the par défaut), configurer trailingSlash, and pick an adapter to match — static pour content, node/vercel/cloudflare pour SSR.

Evidence for this claim The article's described svelte-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: SvelteKit page options 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

Svelte n’est pas SvelteKit — and pour le SEO that’s everything

Svelte is a compiler. It turns votre .svelte components into lean vanilla JavaScript with aucun virtual DOM and aucun runtime library shipped to le navigateur. Que donne Svelte a réel performances edge — but it’s a bundle-size optimization, pas a rendering un. A plain Svelte app (Vite-bundled, aucun meta-framework) is encore a client-side-rendered SPA: le serveur renvoie a blank HTML shell and le navigateur constructs lune page. View source on un and votre content isn’t là.

Ce is the #1 source of confusion quand personnes search “Svelte SEO.” The compile step doesn’t put content in the HTML. SvelteKit fait. SvelteKit is Svelte’s official meta-framework — the equivalent of Suivant.js pour React or Nuxt pour Vue — and it renders pages on le serveur by par défaut, shipping entièrement populated HTML avant quelconque JavaScript runs. It adds SSR, static prerendering, file-based routing, charger functions, and deployment adapters. The headline: bare Svelte = CSR-only; SvelteKit = SSR-first. Everything sinon in ce guide assumes SvelteKit.

Two scoping notes worth stating precisely, parce que they’re où generic “Svelte SEO” advice goes incorrect: SvelteKit’s rendering options (ssr, csr, prerender, trailingSlash) are définir per route and inherit hierarchically — a +layout.js or +layout.server.js peut définir a par défaut pour everything beneath it, and a child route peut override it. So “is this SvelteKit site SEO-friendly” isn’t a project-level question; vérifier the spécifique route. And of SvelteKit’s rendering options, it’s specifically ssr: false — pas SSR being merely absent, and pas “using SvelteKit” in general — que the docs tie to an vide output: “Si vous définir ssr to false, it renders an vide ‘shell’ page à la place.” Ce guide is current pour Svelte 5.x and SvelteKit 2.x (the current majors as of ce mettre à jour); Svelte 5 made runes the par défaut reactivity model, but runes are a component-authoring concern, pas a rendering mode — ils don’t modifier quelconque of the SEO guidance ci-dessous.

Pourquoi the rendering mode matters comes straight from how moteur de recherches fonctionner. Google processes JavaScript in three phases — exploration, rendering, and indexation — and “tout pages with a 200 Code d’état HTTP are sent to the rendering queue, aucun matter si JavaScript is présent on lune page.” The render queue adds a delay, and as Google’s propre guidance puts it, “server-side or pre-rendering is encore a great idea parce que it rend votre website faster pour utilisateurs and robots d’exploration, and pas tout bots peut run JavaScript.” Que dernier clause is doing a lot of fonctionner in 2026 — voir the AI-crawler section.

The four rendering modes in SvelteKit

SvelteKit’s strength is que rendering is a per-route decision viune page options. Ces options are hierarchical: définir a par défaut in a +layout.js/+layout.server.js and every nested route inherits it, unless a child route exports its propre valeur to override it. Vérifier the option que en réalité s’applique to the spécifique route vous care à propos de — pas simplement the valeur définir at the top of the project.

  • SSR (the par défaut). Pages are rendered server-side and the complet HTML is in the initial réponse. Meilleur pour dynamic, personalized, or frequently modification content. Ce is on unless vous turn it off.
  • Prerender (export const prerender = true). Pages are generated as static HTML at construire temps. Maximum speed and maximum crawlability — ideal pour blog posts, docs, and marketing pages. Crucially, prerendering is SSR run at construire temps, so SSR doit remain enabled pour it to fonctionner.
  • SPA fallback (ssr: false). A single-page-app shell with aucun server rendering. The SvelteKit docs are blunt que ce mode “has grand negative performances and SEO impacts” and is really meant pour choses comme wrapping in a mobile app — pas pour content vous vouloir indexé.
  • Hybrid. Mix modes per route: prerender votre marketing pages, SSR votre product pages, run an SPA-style admin behind a login. Ce is SvelteKit’s killer fonctionnalité — vous don’t pick un rendering mode pour the whole site.

The ssr: false trap (the mistake to éviter)

Ce is the la plupart expensive misunderstanding in SvelteKit SEO, so it obtient its propre section. Personnes reach pour adapter-static to “make a static site,” and alors définir ssr: false thinking they’re getting prerendered HTML. Ils aren’t.

Prerendering is rendu côté serveur executed at construire temps. Si vous disable SSR, là n’est pashing to render — the docs are explicit que ssr: false “renders an vide ‘shell’ page à la place,” and that’s exactly ce que adapter-static alors ships as votre prerendered output. Le contenu seulement apparaît une fois the client-side JavaScript runs, qui puts vous correct back in CSR territory (with tout its robot d’exploration problems) despite having a “static” construire. The rule: with adapter-static, leave ssr on (its par défaut) so prerendering outputs réel HTML. Utiliser ssr: false seulement quand vous genuinely vouloir an SPA shell and accept the SEO cost.

Managing metadata with <svelte:head>

SvelteKit has a special element, <svelte:head>, que injects content into the document <head> — and vous don’t besoin a third-party library to manage votre title and meta tags. Every page devrait have a unique <title> and <meta name="description">, plus canonical, Ouvrir Graph, Twitter Card, hreflang, robots directives, and JSON-LD as nécessaire.

The pattern the official docs recommend pour dynamic metadata:

  1. Retourner SEO metadata from a +page.server.js load() function.
  2. Accès it via page.data in votre layout.
  3. Render it in <svelte:head> in the root +layout.svelte.
<!-- +layout.svelte -->
<script>
  import { page } from '$app/state';
</script>

<svelte:head>
  <title>{page.data.title}</title>
  <meta name="description" content={page.data.description} />
  <link rel="canonical" href={page.data.canonical} />
</svelte:head>

Si you’d plutôt utiliser a component wrapper, the third-party svelte-seo package wraps <svelte:head> with props pour the courant tags — but it’s a convenience, pas a requirement.

<svelte:head> obtient votre tags into the head; it doesn’t vérifier vous got les correct. That’s encore votre job: confirmer every route renders a unique title and description (pas un inherited from the layout by accident), que the canonical vous emit is consistent entre the direct-request HTML and ce que a client-side navigation renders, and que robots directives and JSON-LD are présent in the raw réponse — pas seulement visible après hydration.

Charger functions: où vous récupérer SEO données matters

Ce is the metadata bug que bites personnes. +page.server.js load() runs on the server, so its données is in the initial HTML réponse. +page.js load() runs on le serveur pour the premier render and on the client during client-side navigation. The mistake is fetching votre title/description/canonical in a <script> block with onMount() — que runs client-side seulement, so the initial HTML ships with aucun metadata and the robot d’exploration sees nothing on the premier récupérer. Récupérer SEO-critical données in a load function (server charger pour anything que doit be in the premier réponse), pas in onMount.

Don’t assume the fichier nom alone proves the boundary, though — +page.js load() aussi runs on le serveur pour the premier requête, alors re-runs client-side on navigation, and its retourner valeur has to survive serialization to be reused safely on the client. Si SEO-critical données ever dépend on a valeur que isn’t safely serializable, vérifier les deux the direct-request HTML and a client-navigated view of the même route, pas simplement un or the autre.

Routing and Structure d’URL

  • File-based routing with [param] pour dynamic segments donne vous clean, predictable URLs.
  • History API routing is the par défaut — SvelteKit doesn’t utiliser hash/fragment routing, and that’s exactly ce que vous vouloir. Google can’t reliably resolve hash-based (#/page) URLs, so History API routing is the SEO-safe choice and SvelteKit rend it the par défaut.
  • trailingSlash in svelte.config.js accepts 'always', 'never', or 'ignore'. SvelteKit handles the canonical redirection pour vous, but leaving it misconfigured (surtout 'ignore') peut créer duplicate-content variants. Pick un and be consistent.
  • Dynamic routes vous vouloir prerendered besoin an entries function to enumerate the paths at construire temps, or SvelteKit won’t know qui URLs to generate.

Choosing an adapter pour le SEO

The adapter decides how and où votre SvelteKit app is deployed, and que has SEO consequences (mostly via TTFB, qui feeds LCP):

AdapterRenderingSEO implication
adapter-staticComplet SSGVrai static HTML pour every page; aucun server nécessaire; ideal pour content-heavy sites (garder ssr on!)
adapter-nodeSSR on a Node serverDynamic SSR, complet flexibility; vous run a server
adapter-vercelSSR + edgeSSR with optional edge functions; edge TTFB helps LCP
adapter-cloudflareSSR on WorkersEdge SSR globally — potentially the fastest TTFB/LCP
adapter-netlifySSR + CDNSimilaire profile to Vercel

Pour a blog or docs site, adapter-static with prerendering donne vous maximum speed and crawlability. Pour dynamic sites, adapter-cloudflare or adapter-vercel put SSR at the edge, qui peut shrink TTFB and aider Plus grand affichage de contenu — but the adapter seulement picks the deployment shape. Réel TTFB dépend on votre données fetching, mise en cache, and the runtime’s cold-start behavior, so mesurer the deployed page plutôt que assuming the adapter alone delivers the win.

An adapter is a boundary, pas simplement a deploy target: streaming, the filesystem, mise en cache, edge/runtime APIs, redirections, and error handling peut tout behave differently entre adapters. A route que semble correct with the local dev server or adapter-node isn’t guaranteed to behave identically une fois deployed via adapter-cloudflare or adapter-vercel — tester the production construire on the réel target, pas simplement npm run preview.

Sitemaps and robots.txt

SvelteKit has aucun built-in sitemap or robots.txt — comme a headless setup, vous construire les explicitly.

  • Sitemap, manual: créer src/routes/sitemap.xml/+server.js que renvoie the XML. Ajouter export const prerender = true si you’re on adapter-static.
  • Sitemap, dynamic: a server-rendered endpoint que requêtes votre CMS/database and renvoie fresh XML on chaque requête — meilleur pour grand or fast-changing sites.
  • Sitemap, packages: svelte-sitemap scans routes post-build (pour SSG), and sveltekit-static-sitemap generates from prerendered routes.
  • robots.txt: drop a static fichier in static/ (served at /robots.txt automatically) or generate it from a src/routes/robots.txt/+server.js endpoint. Whichever vous choisir, don’t block votre .js/.css — Google won’t render from blocked fichiers.

Performances and Core Web Vitals

SvelteKit’s compiler and framework mechanics give vous a structural head commencer on performances, and Core Web Vitals are a ranking input — but the mechanics themselves don’t guarantee a score. The compiler outputs vanilla JS with aucun virtual DOM and aucun runtime library, so a typical SvelteKit page ships moins JavaScript que an equivalent React-based app; automatic per-route code splitting, built-in asset and lien preloading, file-hashing pour long-lived mise en cache, and edge deployment via several adapters are tout disponible. Ce que vous construire with ceux mechanics — page weight, image handling, third-party scripts, hydration cost, and the deployment target vous en réalité choisir — encore decides votre mesuré CWV and votre rankings. Treat the framework as removing obstacles, pas as delivering le résultat. The SvelteKit docs point vous at the correct measurement outils: “Google’s PageSpeed Insights and WebPageTest are excellent façons to comprendre the performances characteristics of a site.” Image optimization is disponible via @sveltejs/enhanced-img.

AI robots d’exploration and the SSR imperative

Here’s the 2026 wrinkle the older SvelteKit SEO guides miss. Rendering behavior pour GPTBot (OpenAI), ClaudeBot (Anthropic), PerplexityBot, and the Bingbot récupérer behind Copilot is provider- and version-specific, and current provider documentation doesn’t establish un shared render step vous pouvez rely on. Treat “généralement récupère static HTML sans executing JavaScript” as the safe planning assumption, pas a guarantee à propos de every provider forever. A CSR route (bare Svelte, or SvelteKit with ssr: false) ships every un of ces robots d’exploration the même vide shell a first-fetch Googlebot requête sees; SvelteKit’s par défaut SSR puts votre content in the raw HTML so aucun robot d’exploration has to depend on a render step at tout. That’s the argument pour keeping SSR on pour anything vous vouloir AI systems to voir — pas a promise que SSR guarantees inclusion in an AI réponse, qui dépend on factors bien au-delà rendering.

Courant SvelteKit SEO mistakes

  • Fetching SEO données in onMount() au lieu de a server charger function — metadata isn’t in the initial HTML.
  • Shipping SPA fallback mode (ssr: false) pour content vous vouloir ranked.
  • adapter-static with ssr: false — vide shells, pas prerendered pages.
  • Blocking JS/CSS in robots.txt — breaks rendering.
  • Skipping trailingSlash configuration — duplicate-content variants.
  • En utilisant hash/fragment routing — Googlebot can’t resolve ceux URLs (SvelteKit’s History API par défaut déjà protects vous ici).

Si you’re coming at ce from the broader framework angle, Svelte/SvelteKit is un of the decoupled frontends a CMS headless pairs with, and the rendering-mode logic ici is the même logic que governs JavaScript SEO généralement — ceux topics live alongside ce un in the cluster.

Add an expert note

Pin an expert quote

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