agentleFS
full-stack-skillsfull-stack-skills/svelte-skills/skills/sveltekit-advanced/SKILL.md

sveltekit-advanced

Skill4 starsChanged 55 days agoApache-2.0View on GitHub

SvelteKit 高级功能指南 - 状态管理、远程函数(Remote Functions)、环境变量、Hooks、错误处理、链接选项、Service Workers、服务端模块、快照(快照/Shallow Routing)、$app/* 模块、$lib、$service-worker。

  • Reads credentials

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 4 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use this skill when you need to:

    line 15
  2. Distinctive:

    Read env vars safely (private vs public, static vs dynamic).

    line 19
  3. Distinctive:

    Add a service worker for offline / precaching.

    line 23
  4. Very specific:

    Use the $app/* modules (forms, navigation, state, paths, server, environment, types).

    line 26
  5. Distinctive:

    Do NOT use this skill for routing, form actions (legacy), load, basic setup, adapters configuration beyond env vars, or CSS/styling.

    line 28
  6. Very specific:

    Import from $app/env/private or $app/env/public.

    line 96
  7. Very specific:

    Place src/service-worker.js (or src/service-worker/index.js) — bundled and auto-registered.

    line 151

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

SvelteKit Advanced

Advanced SvelteKit features reference. Covers state management across server/client, the remote functions API, env vars (legacy and explicit), hooks (server/shared/universal), error handling, link options, service workers, server-only modules, snapshots, shallow routing, and the $app/* modules.

Source: official SvelteKit llms.txt documentation.

When to use this skill

Use this skill when you need to:

  • Choose where state lives (server context, URL, snapshot, store).
  • Build type-safe client/server RPC with query/form/command/prerender.
  • Read env vars safely (private vs public, static vs dynamic).
  • Customize request handling via hooks (handle, handleFetch, handleError, handleValidationError, reroute, transport, init).
  • Throw expected/unexpected errors, customize the fallback error page, type error shape via App.Error.
  • Configure link behavior with data-sveltekit-* attributes.
  • Add a service worker for offline / precaching.
  • Prevent secret leak via .server.* or $lib/server/.
  • Persist ephemeral DOM state with snapshots or route history entries with shallow routing.
  • Use the $app/* modules (forms, navigation, state, paths, server, environment, types).

Do NOT use this skill for routing, form actions (legacy), load, basic setup, adapters configuration beyond env vars, or CSS/styling.

Critical sections

1. State management

  • No shared server state — module-level variables (let user;) leak across requests. Authenticate via cookies, persist to DB.
  • Pure load functions — no side effects (no global stores). Return data instead.
  • Context for per-request state — setContext('user', () => data.user) (pass a function so reactivity crosses boundaries). Reading context updated during SSR in a child does NOT propagate to the parent (it has already rendered).
  • Component state is preserved across nav — const derived from data only computes once. Use $derived(...) for values that should recompute when data changes.
  • URL state — search params for filters/sort; survives reload, affects SSR.
  • Snapshots — disposable UI state (e.g., "is accordion open?") bound to history entry.

2. Remote functions (since 2.27, experimental)

Opt in via svelte.config.js:

kit: { experimental: { remoteFunctions: true } },
compilerOptions: { experimental: { async: true } }

Flavors exported from *.remote.js:

FlavorPurposeKey features
queryRead dynamic server dataDedup, refresh(), loading/error/current
query.batchBatch n+1 queries in one callReturns (input, idx) => Output
query.liveReal-time async iterableconnected, reconnect(); first value serialized for SSR
formProgressive-enhanced <form><form {...createPost}>, createPost.fields.x.as('text'), validate(), enhance(), for(id), preflight(schema)
commandImperative mutation from event handlersCannot be called during render
prerenderBuild-time datainputs, dynamic: true
  • Validate with any Standard Schema (Zod/Valibot).
  • Args/returns serialized via devalue.
  • getRequestEvent() works inside remote functions for cookies.
  • redirect(...) allowed in query/form/prerender, NOT in command.
  • Single-flight mutations: getPosts().refresh() or getPost(id).set(...) in server handler; requested(getPosts, 1).refreshAll() for client-requested refreshes (limit is DoS protection).

3. Environment variables

Legacy $env/* (default before SvelteKit 2.63):

ModuleScopeTiming
$env/dynamic/privateServer onlyRuntime (process.env-like)
$env/dynamic/publicPublic (PUBLIC_*)Runtime
$env/static/privateServer onlyBuild time (inlined)
$env/static/publicPublicBuild time (inlined)

Explicit env vars (opt-in, default in v3):

Enable in svelte.config.js: kit.experimental.explicitEnvironmentVariables = true. Then create src/env.ts:

import { defineEnvVars } from '@sveltejs/kit/hooks';
import * as v from 'valibot';
import { building } from '$app/env';

export const variables = defineEnvVars({
  API_KEY: {},                              // private
  GOOGLE_ANALYTICS_ID: { public: true },    // public
  SHOW_DEBUG_OVERLAY: { public: true, static: true },  // inlined, dead-code-eliminated
  SECRET: { schema: building ? v.optional(v.string()) : v.string() },
  CACHE_TTL_SECONDS: { description: 'How long...' }
});

Import from $app/env/private or $app/env/public. $app/environment is renamed to $app/env.

4. Hooks

Three optional files: src/hooks.server.js, src/hooks.client.js, src/hooks.js.

Server (hooks.server.js):

  • handle({ event, resolve }) — runs on every request; return Response or call resolve(event, opts). resolve opts: transformPageChunk, filterSerializedResponseHeaders, preload. Use sequence(...) for multiple.
  • handleFetch({ request, fetch, event }) — rewrite cross-origin requests to internal APIs; cookie forwarding for sibling subdomains.
  • handleValidationError({ event, issues }) — customize 400 response for bad remote function args.

Shared (hooks.server.js AND hooks.client.js):

  • handleError({ error, event, status, message }) — for unexpected errors only. Return { message, ... } -> becomes page.error. Must never throw. Server type: HandleServerError; client type: HandleClientError; client event is NavigationEvent.
  • init() — runs once at startup; useful for DB connections.

Universal (hooks.js):

  • reroute({ url, fetch }) — translate URL to a different route (e.g., i18n). Pure/idempotent. Can be async since 2.18.
  • transport — custom encoders/decoders for types crossing the server/client boundary (e.g., Vector).

5. Errors

  • Expected errors — error(404, 'Not found') (or error(404, { message, code })) from @sveltejs/kit. Renders nearest +error.svelte, sets status code. page.error = the object passed.
  • Unexpected errors — any other exception. Not exposed (generic Internal Error); routed through handleError.
  • Rendering errors — opt in via experimental.handleRenderingErrors. Error passed directly to +error.svelte as error prop (not via page.error).
  • Responses — custom src/error.html with %sveltekit.status% and %sveltekit.error.message%. Errors in root +layout.server.js use fallback page (root contains +error.svelte).
  • Type safety — declare App.Error interface in src/app.d.ts:
declare global {
  namespace App {
    interface Error { message: string; code: string; id: string; }
  }
}

6. Link options

data-sveltekit-* attributes on <a> (or parent). Also apply to <form method="GET">.

AttributeValuesEffect
preload-datahover (default), tapPreload load data on hover/tap
preload-codeeager, viewport, hover, tapPreload route code only
reloadbooleanForce full-page nav (also rel="external")
replacestatebooleanReplace history entry instead of push
keepfocusbooleanKeep focus on the same element after nav
noscrollbooleanDisable scroll-to-top after nav

Disable in subtree with data-sveltekit-preload-data="false". Respects navigator.connection.saveData.

7. Service workers

Place src/service-worker.js (or src/service-worker/index.js) — bundled and auto-registered. Disable via config to register manually.

Available from $service-worker: base, build, files, prerendered, version.

Standard pattern: cache build + files on install, network-first with cache fallback on fetch. Skip responses with Cache-Control: no-store (live queries). build/prerendered are empty arrays in dev.

8. Server-only modules

  • $env/static/private and $env/dynamic/private — server-only.
  • $app/server — server-only.
  • Your modules — add .server.js suffix OR place under $lib/server/.

Illegal imports from browser code error at build. Use import type for type-only. Detection is disabled in tests (process.env.TEST === 'true').

9. Snapshots & shallow routing

Snapshots — export snapshot = { capture, restore } from +page.svelte or +layout.svelte. Captured to sessionStorage before page updates; restored on history nav. Must be JSON-serializable. Don't return huge objects.

Shallow routing — pushState(url, state) / replaceState(url, state) create history entries without navigating. Read via page.state. Use preloadData(href) to grab load data, then pushState(href, { selected: result.data }) to render another +page.svelte inside a modal. page.state is always {} on first SSR and during first paint.

Quick Fixes

SymptomFix
User data leaks between requestsDon't use module-level vars; use cookies + DB
load data not updating on navUse $derived(...) not const for values derived from data
Cannot import $lib/server/...Move shared types to import type
Live query stream stopsReconnect with .reconnect(); don't cache no-store responses in SW
error(404, ...) shows blank pageAdd +error.svelte nearest to the route
Server-only env leaked to clientUse $env/static/private or .server naming
Validation 400 generic messageImplement handleValidationError
Component state lost on navWrap with {#key page.url.pathname}<X/>{/key} to force remount

Gotchas

  • load must be pure — no global store writes. Return the data instead.
  • Context updates during SSR don't propagate up — pass state down to avoid flash on hydration.
  • query.batch returns a single function that maps individual inputs to outputs; not a direct array of results.
  • query.live on SSR returns ONLY the first yielded value then closes.
  • command cannot be called during render — invoke from event handlers.
  • requested requires a limit — DoS protection; pass Infinity only if explicitly safe.
  • error() no longer needs throw in SvelteKit 2.x.
  • page.state is {} on SSR and first paint — don't rely on it for critical render.
  • reroute must be pure/idempotent — its result is cached per URL on the client.
  • Service worker build/prerendered empty in dev — test in production build.
  • Cookie forwarding for sibling subdomains requires manual handleFetch — SvelteKit can't tell which parent-domain cookie belongs to which subdomain.
  • handleError must not throw — wrap risky work in try/catch.

FAQ

Q: $app/state vs $app/stores? A: $app/state (since 2.12) is rune-based and reactive. $app/stores is the legacy store-based version. Use $app/state with Svelte 5.

Q: query vs prerender? A: query for dynamic data; prerender for build-time-frozen data that can be served from a CDN. query cannot be used on a fully prerendered page.

Q: form vs command? A: form is progressive-enhanced — works without JS, spreads onto <form>. command is JS-only, called from event handlers. Prefer form.

Q: $env/dynamic vs $env/static? A: dynamic reads at runtime (e.g., process.env); static inlined at build time (enables dead-code elimination). Use static for build-time-known values.

Q: $env/static/private vs $env/dynamic/private? A: Same access restriction; difference is when the value is read. Static gives DCE; dynamic allows runtime override (e.g., MY_FLAG=1 npm run dev).

Q: Can I use $lib/server modules from +page.svelte? A: No — server-only modules cannot be imported by client code, transitively. SvelteKit errors at build.

Q: How do I keep focus on a search input after submit? A: Add data-sveltekit-keepfocus to the <form>.

Q: How do I throw a 404 from a load? A: import { error } from '@sveltejs/kit'; error(404, 'Not found'); (don't throw in SvelteKit 2).

Q: Should I use Sentry? A: Yes — initialize in handleError of both hooks.server.js and hooks.client.js. Server uses HandleServerError; client uses HandleClientError with NavigationEvent.

Examples

TopicFile
State management with context + URL + derivedexamples/state-management.md
Remote query/form/command/batch/liveexamples/remote-functions.md
Env vars (legacy + explicit)examples/env-vars.md
Server + universal hooksexamples/hooks.md
Expected/unexpected errors + App.Errorexamples/errors.md
data-sveltekit-* link optionsexamples/link-options.md
Service worker precache + offlineexamples/service-worker.md
$app/* modules (forms, navigation, state, paths, server)examples/app-modules.md
Snapshots + shallow routingexamples/shallow-snapshots.md

References

TopicFile
State managementreferences/state-management.md
Remote functionsreferences/remote-functions.md
Environment variablesreferences/env-vars.md
Hooksreferences/hooks.md
Errorsreferences/errors.md
Link optionsreferences/link-options.md
Service workersreferences/service-worker.md
Server-only modulesreferences/server-only.md
$app/* modulesreferences/app-modules.md
Snapshots & shallow routingreferences/shallow-snapshots.md

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.