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 Config
When to use this skill
Use this skill whenever you are working on SvelteKit's configuration, build pipeline, deployment, performance, images, accessibility, SEO, debugging, or migration concerns. This includes:
- Choosing or configuring an adapter (
adapter-node,adapter-static,adapter-cloudflare,adapter-netlify,adapter-vercel) - Building (
vite build) and previewing (vite preview) production apps - Advanced routing (rest params, optional params, matchers, route sorting, filename encoding)
- Advanced layouts (route groups, breaking out of layouts with
@) - Auth integration patterns (sessions vs tokens, hooks-based auth)
- Performance optimization (code splitting, asset opt, avoiding waterfalls)
- Image handling (
@sveltejs/enhanced-img, CDN loading,<picture>/<img>patterns) - Accessibility (route announcements, focus management,
langattribute) - SEO (titles, meta tags, sitemaps, JSON-LD)
- Breakpoint debugging (VS Code, Chrome DevTools)
- Migrating from SvelteKit v1 or Sapper
For routing/data/load basics, see sveltekit-overview. For Svelte components/runes, see svelte-runes.
Critical: Building and previewing
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)
Critical: Adapters — when to use which
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() } };
Platform-specific context
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.
Critical: Advanced routing
SvelteKit routes are filesystem-based. Beyond basic dynamic segments, you have several advanced features.
Rest parameters — [...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.
Optional parameters — [[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.
Matching — [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.
Sorting
When multiple routes match, SvelteKit sorts by:
- More specific routes win (fewer params = more specific)
- Matchers (
[name=type]) beat unconstrained ([name]) [[optional]]and[...rest]are lowest priority unless they're the final segment- Ties resolved alphabetically
Encoding special characters
Use 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/:-)
Critical: Advanced layouts
By default, the layout hierarchy mirrors the folder hierarchy. Use these patterns to reshape it.
Route groups — (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
Breaking out — [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.
Reset to root
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
Critical: Auth integration
Auth = authentication (who is this?) + authorization (what can they do?).
Sessions vs tokens
- Sessions: ID stored in DB. Revocable instantly, requires DB lookup per request.
- JWT tokens: Self-contained, no DB lookup, but cannot be revoked immediately. Better latency.
Integration pattern
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);
}
Libraries
npx sv add better-auth— Better Auth integration via Svelte CLI- Lucia auth guide — reference SvelteKit examples for session-based auth
Always require path: '/' when calling cookies.set(...) in SvelteKit v2.
Critical: Performance optimization
SvelteKit ships with: code-splitting, asset preloading, file hashing, request coalescing, parallel loading, data inlining, conservative invalidation, link preloading. To go further:
Diagnose
- PageSpeed Insights / Lighthouse / WebPageTest
- Chrome DevTools Network + Performance tabs
- Test in preview mode (after
vite build), not dev mode
Assets
- Use
@sveltejs/enhanced-imgfor images (smaller formats, intrinsic dimensions) - Lazy-load below-the-fold videos with
preload="none" - Subset fonts; preload critical fonts via
handlehook'spreloadfilter
Code size
- Use Svelte 5 (smaller than 4)
- Use
rollup-plugin-visualizerto find heavy packages - Prefer dynamic
import()for conditional code - Push third-party scripts to web workers (Partytown)
Avoid waterfalls
- Use server
loadfunctions for backend calls (avoid client → server → backend chains) - Issue parallel queries with
Promise.all/ DB joins - SPA mode causes waterfalls — prerender instead
Hosting
- Deploy frontend near backend (or use edge)
- Ensure HTTP/2+
Critical: Images
Vite's built-in handling
<script>
import logo from '$lib/assets/logo.png';
</script>
<img alt="logo" src={logo} />
Vite hashes the filename and inlines small assets.
@sveltejs/enhanced-img
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" />
Dynamic CDN loading
For images unavailable at build time (CMS, DB), use a CDN library:
@unpic/svelte— CDN-agnosticsvelte-cloudinary— Cloudinary- CMS-bundled: Contentful, Storyblok, Contentstack
Best practices
- Set
fetchpriority="high"and avoidloading="lazy"for LCP images - Always provide
alttext - Don't use
em/reminsizes - Mix strategies: Vite for
<meta>, enhanced-img for hero, CDN for user content
Critical: Accessibility
SvelteKit provides an accessible foundation; you're still responsible for app-level a11y.
Route announcements
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>
Focus management
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 attribute
Set <html lang="en"> (or your language) in src/app.html. For multi-language sites, use a transformPageChunk in handle to set per-request.
Critical: SEO
SvelteKit ships with SSR, normalized trailing-slash URLs, and good defaults. Manual steps:
Per-page meta
<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>.
Sitemap
// 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' } });
}
JSON-LD structured data
Inject via <svelte:head> with <script type="application/ld+json">.
Critical: Migration
SvelteKit v1 → v2
Use npx sv migrate sveltekit-2 (or npx svelte-migrate for older projects). Breaking changes:
error(...)andredirect(...)are no longer thrown — just call themcookies.set/delete/serializerequirespath: '/'- Top-level promises in
loadare NOT awaited automatically — useawaitexplicitly gotono longer accepts external URLs (usewindow.location.href)pathsare now consistently relative (defaulttrue)$app/storesdeprecated in 2.12 — migrate to$app/state(Svelte 5 runes)- Svelte 4 required, Node 18.13+
vitePreprocessmust be imported from@sveltejs/vite-plugin-svelte
Sapper → SvelteKit
package.json: add"type": "module", removepolka/sapper/sirv/compression- Add
@sveltejs/kit+ an adapter - Scripts:
sapper build→vite build,sapper dev→vite dev,node __sapper__/build→node build src/template.html→src/app.html(replace%sapper.*placeholders)- Routes:
routes/about/index.svelte→routes/about/+page.svelte _layout.svelte→+layout.svelte,_error.svelte→+error.sveltepreload→load(different API: singleeventarg, nothis.fetch)stores/navigatingfrom$app/stores(or$app/statein 2.12+)- Regex routes → matchers in
src/params/ sapper:prefetch→data-sveltekit-preload-datasapper:noscroll→data-sveltekit-noscroll- Move internal libs from
src/node_modules/...tosrc/lib
Critical: Debugging
VS Code
Built-in debug terminal works out of the box:
CMD/Ctrl+Shift+P→ "Debug: JavaScript Debug Terminal"- Run
npm run devin that terminal - Set breakpoints, hit them in the browser
Or create .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{ "command": "npm run dev", "name": "dev", "request": "launch", "type": "node-terminal" }
]
}
Chrome DevTools / Edge
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.
Quick Fixes
buildingflag: Wrap one-time init code inif (!building)to prevent it running duringvite build.fallbackinadapter-static: Use200.htmlfor SPA mode, but avoidindex.html(conflicts with prerendered/).- GitHub Pages: Set
paths.baseto repo name andfallback: '404.html'. Add.nojekylltostatic/. - Node origin: Set
ORIGINenv var if you're behind a proxy and see "Cross-site POST form submissions are forbidden". - XFF depth: Behind N proxies, set
XFF_DEPTH=NsogetClientAddress()returns the real client IP. - Compression: Use
@polka/compressionnotcompression— SvelteKit streams responses. .envin production: Production doesn't auto-load.env. Usenode --env-file=.env build(Node 20.6+) or-r dotenv/config.platformonly in dev/preview: Test Cloudflare/Netlify/Vercel platform APIs with their respective CLIs (wrangler dev,netlify dev,vercel dev).fsnot available: Usereadfrom$app/server(works in edge by fetching from deployed assets).gotoexternal URL: Usewindow.location.hrefinstead.- Cookie path: SvelteKit v2 requires
path: '/'oncookies.set(...). - Trailing slash: Set
trailingSlash: 'always'if your host doesn't serve/a.htmlfrom/a.
Gotchas
- Preview is not production:
vite previewruns in Node, doesn't emulate adapter-specific behavior. Use the platform's CLI for accurate local testing. adapter-autois 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.- Server bundle size on Cloudflare: Workers have a size limit. Move large libraries to client-only imports if you hit it.
- AWS / Node 18: SvelteKit v2 requires Node 18.13+. Older Node versions fail with cryptic errors.
- ISR + prerender: ISR has no effect on routes with
export const prerender = true. adapter-cloudflare-workersdeprecated: Use@sveltejs/adapter-cloudflare(withassets.directory+assets.bindingin wrangler config).- Svelte 4 → 5:
$app/storesis deprecated; use$app/state. Update Svelte first, then SvelteKit. - Tailwind +
<enhanced:img>: Tag-name selectors needenhanced\:imgto escape the colon. - Match
data-sveltekit-noscrollfor chat/SPA-like UIs: Default scroll-to-top behavior can break infinite-scroll apps. - Vite asset inlining: Assets below
assetsInlineLimit(default ~4kb) get base64-inlined. Excludessvgfor enhanced-img.
FAQ
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.
Examples & References
Examples
| 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 |
References
| 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 |