SvelteKit

Pathrule2 Rules • 2 Memories • 1 Skill

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.

Suggested path map

Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.

/ workspace root
sveltekit-review
src/
Svelte 5 runes are the default; classes replace stores
routes/
Server-only data and secrets live in +page.server.ts
Mutations go through form actions or remote form/command, not load
Avoid load waterfalls; stream non-critical data

Rules

2
Server-only data and secrets live in +page.server.ts/src/routeshighstrictPut 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` or a `$env/.../private` module into universal load or client code. Vite will refuse to build it, and that signal means the logic is in the wrong file.
6- When both load functions exist on a route, the server load runs first and its result reaches the universal load via `data`. Universal output cannot flow back to the server.
Mutations go through form actions or remote form/command, not load/src/routeshighadvisoryUse form actions or remote functions for writes; load is read-only and reruns on navigation.
1`load` functions are for reading data and must stay side-effect free, because SvelteKit can rerun them on navigation, invalidation, and SSR.
2 
3- Handle writes with named `actions` in `+page.server.ts` and `<form method="POST">`, or with experimental remote `form` / `command` functions, so they are progressively enhanced and type-safe.
4- Validate input server-side and return `fail(status, data)` on validation errors; return a serializable object on success.
5- Use `enhance` for client-side progressive enhancement instead of hand-rolled `fetch`, and trigger `invalidate` / `invalidateAll` to refresh affected `load` data after a mutation.
6- Do not perform POST/PUT/DELETE logic inside a `load` function or call mutating remote `command`s from one.

Memories

2
Svelte 5 runes are the default; classes replace stores/srcPrefer $state/$derived/$effect/$props; 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, `$effect` only for genuine side effects, and `$props` for component inputs.
4- Reach for `$state` only when a value drives the UI. Plain `const`/`let` is cheaper and clearer for everything else.
5- For shared logic, write a reactive class in `$lib` that uses `$state`/`$derived` on its fields and import it, instead of authoring `writable`/`readable` stores. Runes work inside plain `.svelte.ts` modules and classes.
6- 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.
5- Attach `.catch()` to any streamed promise that does not use SvelteKit's enhanced `fetch`, or an unhandled rejection can crash the response.
6- Use the injected `fetch` argument inside `load` (not global `fetch`) so SvelteKit forwards credentials, resolves relative URLs, and tracks the request as a dependency for `invalidate`. Call `depends('app:key')` for custom clients that bypass `fetch`.

Skills

1
sveltekit-review/rootPre-merge checklist for SvelteKit 2 routes: data boundaries, secrets, runes, and mutations.
1---
2name: sveltekit-review
3description: Review a SvelteKit 2 (Svelte 5) change before merge - verify load placement, secret isolation, runes usage, form actions, and load performance. Use when reviewing or authoring routes, load functions, form actions, or shared $lib reactivity.
4---
5 
6# SvelteKit review
7 
8- [ ] Secrets, DB access, cookies, and `locals` only appear in `+page.server.ts` / `+layout.server.ts`, never in universal load or client code.
9- [ ] No `$lib/server` or `$env/.../private` import reaches a `+page.ts`, `+layout.ts`, or component.
10- [ ] Universal `load` returns only serializable data or intentional non-serializable values (components/classes); server `load` returns serializable data only.
11- [ ] `load` functions are read-only; all writes go through `actions` or remote `form`/`command`.
12- [ ] Form actions validate input server-side and use `fail(status, data)` for errors; `<form>` uses `enhance` for progressive enhancement.
13- [ ] Data is refreshed after mutations via `invalidate` / `invalidateAll` rather than manual refetch.
14- [ ] Reactivity uses runes (`$state`, `$derived`, `$effect`, `$props`); no `$:` statements or new `writable`/`readable` stores where a reactive class fits.
15- [ ] `$effect` is used only for true side effects, not for deriving values that `$derived` should compute.
16- [ ] `load` uses the injected `fetch` argument; independent fetches start before `await parent()`.
17- [ ] Slow non-critical data is streamed via returned promises with `.catch()` handlers where needed.

Why this pattern

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

Built for Teams shipping full-stack apps on SvelteKit 2 with Svelte 5.

Keeps your assistant from:

  • 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
License
Apache-2.0
Version
1.0.0
Updated
2026-06-09
View source