sveltekit-config
SvelteKit 配置/构建/部署/性能技能。当用户配置 adapter(node/static/cloudflare/netlify/vercel)、使用 advanced routing/layouts、优化性能(代码分割/asset/hydration)、处理 images(@sveltejs/enhanced-img)、实现 accessibility/SEO、调试 SvelteKit 应用、从 SvelteKit v1/Sapper 迁移时使用。
SvelteKit 配置/构建/部署/性能技能。当用户配置 adapter(node/static/cloudflare/netlify/vercel)、使用 advanced routing/layouts、优化性能(代码分割/asset/hydration)、处理 images(@sveltejs/enhanced-img)、实现 accessibility/SEO、调试 SvelteKit 应用、从 SvelteKit v1/Sapper 迁移时使用。
Use this skill whenever you are working on SvelteKit's configuration, build pipeline, deployment, performance, images, accessibility, SEO, debugging, or migration concerns. This includes:
adapter-node, adapter-static, adapter-cloudflare, adapter-netlify, adapter-vercel)vite build) and previewing (vite preview) production apps@)@sveltejs/enhanced-img, CDN loading, <picture>/<img> patterns)lang attribute)For routing/data/load basics, see sveltekit-overview. For Svelte components/runes, see svelte-runes.
vite build runs in two stages: Vite produces an optimized production build, then your adapter tailors the output for the target platform. Prerendering executes during build.
During the build, SvelteKit loads your +page/layout(.server).js for analysis. Code that must NOT run at build time should guard with building from $app/environment:
import { building } from '$app/environment';
import { initialiseDatabase } from '$lib/server/database';
if (!building) initialiseDatabase();
export function load() { /* ... */ }
After building, run vite preview (or npm run preview) to test the production build locally. Preview runs in Node, so adapter-specific behavior (e.g. Cloudflare's platform object) does NOT apply — use wrangler dev for Cloudflare or the platform's CLI for accurate testing.
npm run build # vite build + adapter
npm run preview # vite preview (Node)
The adapter is configured in svelte.config.js under kit.adapter. adapter-auto ships by default in new projects and picks the right adapter for known deployment environments (Cloudflare Pages, Netlify, Vercel, Azure SWA, SST, Google Cloud Run). Once you've chosen a target, install that adapter explicitly so it lands in your lockfile.
| Target | Adapter | Notes |
|---------------------------------|----------------------------------|-------|
| Node server / Docker / VM | @sveltejs/adapter-node | Standalone Node server. Most flexible. |
| Static hosting (no SSR) | @sveltejs/adapter-static | SSG or SPA fallback. |
| Cloudflare Workers / Pages | @sveltejs/adapter-cloudflare | Unified adapter for both. |
| Netlify | @sveltejs/adapter-netlify | Node functions or Deno edge. |
| Vercel | @sveltejs/adapter-vercel | Serverless or edge, ISR support. |
Adapter quick guide:
// svelte.config.js
import adapter from '@sveltejs/adapter-node';
export default { kit: { adapter: adapter() } };
Some adapters expose platform info (KV namespaces, Durable Objects, env vars) via event.platform in hooks/server routes. Type augmentation in src/app.d.ts:
declare global {
namespace App {
interface Platform {
env: { MY_KV: KVNamespace };
}
}
}
export {};
Always prefer $env/static/private for environment variables — $env/dynamic/* cannot be used during prerendering.
SvelteKit routes are filesystem-based. Beyond basic dynamic segments, you have several advanced features.
[...rest]Match an unknown number of segments. src/routes/a/[...rest]/z/+page.svelte matches /a/z, /a/b/z, /a/b/c/z. The rest param is a string with /-separated segments.
src/routes/[org]/[repo]/tree/[branch]/[...file]/+page.svelte
Use rest parameters to render custom 404s — add [...path]/+page.js that calls error(404) so a nested +error.svelte is reached.
[[lang]]Wrap with double brackets to make a param optional. [[lang]]/home matches both /home and /en/home. An optional param cannot follow a rest param.
[name=type]Constrain a parameter with a matcher from src/params/:
// src/params/fruit.js
/** @type {import('@sveltejs/kit').ParamMatcher} */
export function match(param) {
return param === 'apple' || param === 'orange';
}
Then write src/routes/fruits/[page=fruit]/+page.svelte. Matchers run on both server and browser.
When multiple routes match, SvelteKit sorts by:
[name=type]) beat unconstrained ([name])[[optional]] and [...rest] are lowest priority unless they're the final segmentUse hex escape [x+nn] in folder names: / → [x+2f], : → [x+3a], etc. Use [u+nnnn] for Unicode (no surrogate pairs needed).
src/routes/smileys/[x+3a]-[x+29]/+page.svelte
# matches /smileys/:-)
By default, the layout hierarchy mirrors the folder hierarchy. Use these patterns to reshape it.
(group)Parentheses-wrapped folder names don't appear in the URL. Use to share a layout between routes without affecting URL structure.
src/routes/
├ (app)/dashboard/+page.svelte
├ (app)/+layout.svelte # app shell
├ (marketing)/about/+page.svelte
├ (marketing)/+layout.svelte # marketing shell
└ +layout.svelte
[email protected]Append @<segment> (or @ for root) to reset the layout chain. +page@(app).svelte inherits only from (app)/+layout.svelte.
Options: +page@[id].svelte, [email protected], +page@(app).svelte, [email protected].
Layouts can also break out: [email protected] rewinds to root for everything below it.
If you want most of your app under one layout but a few routes to escape, put everything inside a group except the outliers:
src/routes/
├ (app)/...
└ admin/+page.svelte # does NOT inherit (app) layout
Auth = authentication (who is this?) + authorization (what can they do?).
Check auth cookies in src/hooks.server.js, populate event.locals.user, then read locals in +page.server.js / +server.js load functions.
// src/hooks.server.js
export async function handle({ event, resolve }) {
event.locals.user = await getUser(event.cookies.get('session'));
return resolve(event);
}
npx sv add better-auth — Better Auth integration via Svelte CLIAlways require path: '/' when calling cookies.set(...) in SvelteKit v2.
SvelteKit ships with: code-splitting, asset preloading, file hashing, request coalescing, parallel loading, data inlining, conservative invalidation, link preloading. To go further:
vite build), not dev mode@sveltejs/enhanced-img for images (smaller formats, intrinsic dimensions)preload="none"handle hook's preload filterrollup-plugin-visualizer to find heavy packagesimport() for conditional codeload functions for backend calls (avoid client → server → backend chains)Promise.all / DB joins<script>
import logo from '$lib/assets/logo.png';
</script>
<img alt="logo" src={logo} />
Vite hashes the filename and inlines small assets.
Build-time image optimization: generates avif/webp, sets intrinsic width/height (prevents CLS), strips EXIF.
// vite.config.js — plugin order matters
import { enhancedImages } from '@sveltejs/enhanced-img';
import { sveltekit } from '@sveltejs/kit/vite';
export default { plugins: [enhancedImages(), sveltekit()] };
Usage:
<enhanced:img src="./image.jpg" alt="..." sizes="min(1280px, 100vw)" />
Generated <picture> includes multiple formats and sizes for HiDPI. Provide 2x source for retina displays.
Custom widths: <enhanced:img src="./image.png?w=1280;640;400" />
Per-image transforms: <enhanced:img src="./image.jpg?blur=15" />
For images unavailable at build time (CMS, DB), use a CDN library:
@unpic/svelte — CDN-agnosticsvelte-cloudinary — Cloudinaryfetchpriority="high" and avoid loading="lazy" for LCP imagesalt textem/rem in sizes<meta>, enhanced-img for hero, CDN for user contentSvelteKit provides an accessible foundation; you're still responsible for app-level a11y.
SvelteKit injects a live region that reads the <title> after each client-side navigation. Every page must have a unique, descriptive <title> in a <svelte:head>:
<svelte:head>
<title>Todo List</title>
</svelte:head>
After each navigation, SvelteKit focuses <body> (or [autofocus] element if present). Override with afterNavigate for custom behavior:
import { afterNavigate } from '$app/navigation';
afterNavigate(() => document.querySelector('.focus-me')?.focus());
Use data-sveltekit-keepfocus on a <form> to preserve input focus. goto(url, { keepFocus: true }) for programmatic nav.
lang attributeSet <html lang="en"> (or your language) in src/app.html. For multi-language sites, use a transformPageChunk in handle to set per-request.
SvelteKit ships with SSR, normalized trailing-slash URLs, and good defaults. Manual steps:
<svelte:head>
<title>Page Title — Site Name</title>
<meta name="description" content="..." />
<meta property="og:title" content="..." />
<meta property="og:description" content="..." />
<meta property="og:image" content="..." />
<meta property="og:type" content="website" />
<link rel="canonical" href="https://..." />
</svelte:head>
Common pattern: return SEO data from load, render in root layout's <svelte:head>.
// src/routes/sitemap.xml/+server.js
export async function GET() {
return new Response(`<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<!-- <url> entries -->
</urlset>`, { headers: { 'Content-Type': 'application/xml' } });
}
Inject via <svelte:head> with <script type="application/ld+json">.
Use npx sv migrate sveltekit-2 (or npx svelte-migrate for older projects). Breaking changes:
error(...) and redirect(...) are no longer thrown — just call themcookies.set/delete/serialize requires path: '/'load are NOT awaited automatically — use await explicitlygoto no longer accepts external URLs (use window.location.href)paths are now consistently relative (default true)$app/stores deprecated in 2.12 — migrate to $app/state (Svelte 5 runes)vitePreprocess must be imported from @sveltejs/vite-plugin-sveltepackage.json: add "type": "module", remove polka/sapper/sirv/compression@sveltejs/kit + an adaptersapper build → vite build, sapper dev → vite dev, node __sapper__/build → node buildsrc/template.html → src/app.html (replace %sapper.* placeholders)routes/about/index.svelte → routes/about/+page.svelte_layout.svelte → +layout.svelte, _error.svelte → +error.sveltepreload → load (different API: single event arg, no this.fetch)stores / navigating from $app/stores (or $app/state in 2.12+)src/params/sapper:prefetch → data-sveltekit-preload-datasapper:noscroll → data-sveltekit-noscrollsrc/node_modules/... to src/libBuilt-in debug terminal works out of the box:
CMD/Ctrl+Shift+P → "Debug: JavaScript Debug Terminal"npm run dev in that terminalOr create .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{ "command": "npm run dev", "name": "dev", "request": "launch", "type": "node-terminal" }
]
}
NODE_OPTIONS="--inspect" npm run dev
Open the site, then click the "Open dedicated DevTools for Node.js" icon (Node logo, top-left). Or visit chrome://inspect.
building flag: Wrap one-time init code in if (!building) to prevent it running during vite build.fallback in adapter-static: Use 200.html for SPA mode, but avoid index.html (conflicts with prerendered /).paths.base to repo name and fallback: '404.html'. Add .nojekyll to static/.ORIGIN env var if you're behind a proxy and see "Cross-site POST form submissions are forbidden".XFF_DEPTH=N so getClientAddress() returns the real client IP.@polka/compression not compression — SvelteKit streams responses..env in production: Production doesn't auto-load .env. Use node --env-file=.env build (Node 20.6+) or -r dotenv/config.platform only in dev/preview: Test Cloudflare/Netlify/Vercel platform APIs with their respective CLIs (wrangler dev, netlify dev, vercel dev).fs not available: Use read from $app/server (works in edge by fetching from deployed assets).goto external URL: Use window.location.href instead.path: '/' on cookies.set(...).trailingSlash: 'always' if your host doesn't serve /a.html from /a.vite preview runs in Node, doesn't emulate adapter-specific behavior. Use the platform's CLI for accurate local testing.adapter-auto is just for zero-config: Once you've decided on a target, install the real adapter so it lands in the lockfile and you can pass options.export const prerender = true.adapter-cloudflare-workers deprecated: Use @sveltejs/adapter-cloudflare (with assets.directory + assets.binding in wrangler config).$app/stores is deprecated; use $app/state. Update Svelte first, then SvelteKit.<enhanced:img>: Tag-name selectors need enhanced\:img to escape the colon.data-sveltekit-noscroll for chat/SPA-like UIs: Default scroll-to-top behavior can break infinite-scroll apps.assetsInlineLimit (default ~4kb) get base64-inlined. Excludes svg for enhanced-img.Should I use adapter-auto?
Yes for prototyping. Once you've chosen a target, swap to the specific adapter so you can configure it.
SPA mode vs full SSR?
Full SSR with adapter-static (prerender) is best for SEO/perf. SPA fallback is for when you must deploy to static-only hosting without prerendering everything. SPA hurts SEO and performance.
When should I prerender?
Pages that return the same content for every visitor (marketing pages, blog posts, docs). Add export const prerender = true to the route.
How do I know which adapter is being used in production?
adapter-auto logs at build time which one it picked. Otherwise, check svelte.config.js.
Can I use the same svelte.config.js for multiple adapters?
No — you must run vite build once per adapter. Common CI pattern: build matrix per target.
Why is my Cloudflare Worker huge?
Bundle bloat — server-side imports pull in large deps. Move them to dynamic import() or client-only.
Do I need Svelte 5 for SvelteKit 2?
You need Svelte 4+. Svelte 5 is recommended for $app/state and runes.
| File | What it covers |
|------|----------------|
| examples/build-preview.md | vite build, vite preview, env vars at build time |
| examples/adapter-node.md | dev/build/deploy, custom server, env vars |
| examples/adapter-static.md | prerender all, SPA fallback, GitHub Pages |
| examples/adapter-cloudflare.md | workers, pages, runtime APIs, env vars |
| examples/adapter-netlify.md | deploy, edge functions, env vars |
| examples/adapter-vercel.md | deploy, image opt, ISR, env vars, skew protection |
| examples/writing-adapter.md | custom adapter using the builder API |
| examples/advanced-routing.md | rest params, optional, matchers, sort, encoding |
| examples/advanced-layouts.md | nested layouts, named layouts, error reset, groups |
| examples/performance.md | code splitting, asset opt, hydration opt |
| examples/images.md | enhanced-img, dynamic CDN loading, best practices |
| examples/accessibility.md | route announcements, focus management, lang attribute |
| examples/seo.md | meta tags, OG tags, JSON-LD, sitemaps |
| examples/debugging.md | VS Code launch.json, browser breakpoints |
| examples/migration-v2.md | error/redirect changes, cookie path, top-level promises |
| File | What it covers |
|------|----------------|
| references/adapters-comparison.md | detailed when-to-use-each-adapter matrix |
| references/building-reference.md | vite build options, preview, env vars |
| references/routing-advanced-reference.md | rest/optional/matchers/sort/encoding deep-dive |
| references/layouts-advanced-reference.md | nested/named/groups, reset, breaking out |
| references/auth-reference.md | sessions vs tokens, integration points, libs |
| references/performance-reference.md | full optimization checklist |
| references/images-reference.md | Vite + @sveltejs/enhanced-img reference |
| references/a11y-reference.md | accessibility patterns and resources |
| references/seo-reference.md | SEO setup, meta tags, structured data |
| references/migration-v1-v2-reference.md | full breaking-changes list |
| references/migration-sapper-reference.md | complete Sapper → SvelteKit migration |
| references/debugging-reference.md | IDE/debugger configs |