本文へスキップ
Pathrule

パターン

このページは英語でのみご覧いただけます。

SvelteKit

Pathruleルール2件 • メモリ4件 • スキル1件

A pattern bundle for SvelteKit 2 apps built on Svelte 5 runes. It encodes where data loading belongs, how to keep secrets server-side, and how form actions and remote functions stay type-safe and progressively enhanced. Use it so AI agents stop leaking private values into the client bundle and stop reaching for stores when runes are the right tool.

推奨されるパスの配置

Pathruleは各要素を対応するパスに配置するため、アシスタントはそれが属する場所でだけ参照します。これはインポート時に適用されるスコープで、ワークスペースで調整できます。

/ ワークスペースのルート
sveltekit-review
src/
Svelte 5 runes are the default; reactive classes replace stores
Async-in-components is experimental; reads after await are not tracked
routes/
Server-only data and secrets live in +page.server.ts
Mutations go through form actions or remote functions, never load
Avoid load waterfalls; stream non-critical data
Remote functions are experimental and the API is still moving

ルール

2
Server-only data and secrets live in +page.server.ts/src/routes高強制Put auth, DB access, and private env in server load; keep universal load for public, serializable data.
1Any data that needs secrets, cookies, a database, or private APIs belongs in a server load function, not a universal one.
2 
3- Use `+page.server.ts` / `+layout.server.ts` for `load` that touches `$env/static/private`, `$env/dynamic/private`, `cookies`, or `locals`.
4- Reserve `+page.ts` / `+layout.ts` (universal) for public external APIs and non-serializable returns like component constructors.
5- Never import anything from `$lib/server`, a `*.server.ts` module, or a `$env/.../private` module into universal load or any client-reachable code. SvelteKit treats the whole import chain as unsafe and Vite refuses to build it; that build error is the signal the logic is in the wrong file, not something to work around.
6- When both load functions exist on a route, the server load runs first and its result reaches the universal load via the `data` property. Universal output cannot flow back to the server.
7- Server load runs only on the server. Universal load runs on the server during SSR, then again in the browser on hydration and on client-side navigation, so it must never assume a server-only global is present.
Mutations go through form actions or remote functions, never load/src/routes高助言Writes belong in form actions or remote form/command; load is read-only and reruns on navigation.
1`load` functions are for reading and must stay side-effect free, because SvelteKit reruns them on navigation, invalidation, and SSR. A write placed in `load` will silently re-fire.
2 
3- Handle writes with named `actions` in `+page.server.ts` plus a `<form method="POST">`, or with remote `form` / `command` functions in a `.remote.ts` file.
4- Validate input server-side. In a form action, return `fail(status, data)` on validation errors and a serializable object on success.
5- Use the `enhance` action for progressive enhancement instead of a hand-rolled `fetch`, and call `invalidate('app:key')` / `invalidateAll()` to refresh affected `load` data after the mutation.
6- Do not perform POST/PUT/DELETE logic inside a `load`, and do not call a mutating remote `command` from a `load`.

メモリ

4
Svelte 5 runes are the default; reactive classes replace stores/srcPrefer $state/$derived/$props; never use $effect to sync state; use reactive classes in $lib over writable stores.
1This codebase targets Svelte 5, so reactivity is rune-based, not store-based.
2 
3- Use `$state` for mutable reactive values, `$derived` (or `$derived.by`) for computed values, `$props` for component inputs, and `$effect` only as an escape hatch for genuine side effects (DOM, analytics, subscriptions).
4- Do not use `$effect` to synchronise or recompute state from other state. The docs are explicit: use `$derived` instead. Writing to state that the same effect reads also risks an infinite loop; reach for `untrack` only when you deliberately need to read without depending.
5- Reach for `$state` only when a value drives the UI. Plain `const` / `let` is cheaper and clearer for everything else.
6- For shared logic, write a reactive class in `$lib` whose fields use `$state` / `$derived`, and import it, instead of authoring `writable` / `readable` stores. Runes work inside plain `.svelte.ts` modules and classes.
7- Avoid `$:` reactive statements and the implicit let-is-reactive model from Svelte 4. They do not exist in runes mode.
Avoid load waterfalls; stream non-critical data/src/routesFire independent fetches before await parent(); return unawaited promises for slow, non-essential data.
1Load performance hinges on not serializing requests that could run in parallel.
2 
3- Start independent `fetch` calls before `await parent()` so they do not block on parent data they do not need.
4- Return unresolved promises from a server `load` for non-essential data. SvelteKit streams them to the client so the page renders before they settle, and the markup can `{#await}` them.
5- Attach `.catch()` to any streamed promise that does NOT come from SvelteKit's injected `fetch`. An unhandled rejection in a streamed promise can crash the response.
6- Use the injected `fetch` argument inside `load` (not global `fetch`) so SvelteKit forwards credentials and cookies, resolves relative URLs, inlines the response during SSR, and tracks the request as a dependency for `invalidate`. For custom clients that bypass `fetch`, call `depends('app:key')` to register a manual dependency.
Remote functions are experimental and the API is still moving/src/routesquery/form/command/prerender in .remote.ts behind two experimental flags; validate args with a Standard Schema; expect breaking churn.
1Remote functions (available since SvelteKit 2.27) are still experimental as of mid-2026. Treat their surface as unstable and pin against the SvelteKit version in this repo before copying examples.
2 
3- Enable them with BOTH flags: `kit.experimental.remoteFunctions: true` and `compilerOptions.experimental.async: true` in `svelte.config.js`. They live in `*.remote.ts` files and export `query` (read, deduped + cached), `form` (progressively-enhanced writes bound to `<form>`), `command` (writes from anywhere, not form-bound), and `prerender` (build-time static data).
4- Every function that takes an argument must validate it with a Standard Schema validator (Zod, Valibot) passed as the first parameter. Do not trust raw input.
5- Use single-flight mutations: inside a `form` / `command` handler, call `.refresh()` / `.set()` on affected queries so the mutation and the data refresh travel in one round-trip. Use `getRequestEvent()` for request context, and `query.batch` to collapse N parallel calls into one request and avoid n+1.
6- Watch the breaking churn: `.run()` was removed from remote queries (await the query directly); `enhance` callbacks now receive a copy of the form instance rather than `{ form, data, submit }`; `query.live()` is the async-iterable real-time subscription helper. Confirm exact signatures against the installed version, not blog posts.
Async-in-components is experimental; reads after await are not tracked/srcawait in script/$derived/markup needs experimental.async; reactive reads after await or in setTimeout are not tracked; use $effect.pending.
1Svelte 5 (since 5.36) lets you use `await` directly in component `<script>`, in `$derived`, and in markup, but it is experimental and must be opted into via `compilerOptions.experimental.async` in `svelte.config.js`. The flag is slated to be removed in Svelte 6.
2 
3- Reactive values read ASYNCHRONOUSLY are not tracked. Anything read after an `await`, inside a `setTimeout`, or in a `.then()` callback will not register as a dependency, so the effect or derived will not re-run when it changes. Read the reactive value synchronously first, then await.
4- Use `$effect.pending()` to know how many async operations are still settling in the current boundary (it does not count child boundaries) when you need loading UI.
5- Svelte holds the UI in a consistent state while an `await` that depends on reactive state is in flight, rather than flashing intermediate values; rely on that instead of manual loading flags where possible.
6- This is independent of SvelteKit `load` streaming. Prefer `load` + streamed promises for route data and reserve in-component `await` for leaf-level async that is local to one component.

スキル

1
sveltekit-review/rootPre-merge checklist for SvelteKit 2 routes: data boundaries, secrets, runes, mutations, and remote functions.
1---
2name: sveltekit-review
3description: Review a SvelteKit 2 (Svelte 5) change before merge - verify load placement, secret isolation, runes usage, form actions, remote functions, and load performance. Use when reviewing or authoring routes, load functions, form actions, remote functions, or shared $lib reactivity.
4---
5 
6# SvelteKit review
7 
8## Secrets and data boundaries
9- [ ] Secrets, DB access, `cookies`, and `locals` appear only in `+page.server.ts` / `+layout.server.ts`, never in universal load or client code.
10- [ ] No `$lib/server`, `*.server.ts`, or `$env/.../private` import reaches a `+page.ts`, `+layout.ts`, or component (the build would fail; if it builds, the chain is clean).
11- [ ] Universal `load` returns only serializable data or intentional non-serializable values (components/classes); server `load` returns serializable data only.
12 
13## Mutations
14- [ ] `load` functions are read-only; all writes go through `actions` or remote `form` / `command`.
15- [ ] Form actions validate input server-side and use `fail(status, data)` for errors; `<form>` uses `enhance`.
16- [ ] Data is refreshed after mutations via `invalidate` / `invalidateAll` (or remote single-flight `.refresh()` / `.set()`), not manual refetch.
17 
18## Reactivity
19- [ ] Reactivity uses runes (`$state`, `$derived`, `$props`); no `$:` statements or new `writable` / `readable` stores where a reactive class fits.
20- [ ] `$effect` is used only for true side effects, never to synchronise or recompute state that `$derived` should produce.
21- [ ] If async-in-components is used, no reactive value is depended on only after an `await` / in a `setTimeout`.
22 
23## Load performance
24- [ ] `load` uses the injected `fetch` argument; independent fetches start before `await parent()`.
25- [ ] Slow non-critical data is streamed via returned promises, with `.catch()` on any promise not from the injected `fetch`.
26 
27## Remote functions (if used)
28- [ ] Both `experimental.remoteFunctions` and `experimental.async` flags are set; functions live in `*.remote.ts`.
29- [ ] Every argument-taking remote function validates input with a Standard Schema (Zod / Valibot).
30- [ ] API usage matches the installed SvelteKit version (no removed `.run()`; correct `enhance` callback shape).

このパターンを使う理由

AI agents leak private values into the client bundle and pick the wrong data-loading or reactivity primitive in SvelteKit.

対象:Teams shipping full-stack apps on SvelteKit 2 with Svelte 5

アシスタントが次のことをしないようにします。

  • Importing $env/static/private or $env/dynamic/private into universal load or client code, leaking secrets into the bundle
  • Putting database queries and auth in +page.ts universal load instead of +page.server.ts
  • Reaching for writable stores where Svelte 5 runes ($state, $derived) are the correct primitive
ライセンス
Apache-2.0
バージョン
1.0.1
更新日
2026-08-25
ソースを見る