git:20260510.fe97ca3 to git:20260905.88628fb

62 added, 55 removed. Audit B to B.

---
name: programming-svelte
title: "Svelte Development"
description: "Svelte 5 runes, SvelteKit architecture, server/client boundaries, and progressive enhancement. Auto-activates in Svelte projects."
license: Apache-2.0
compatibility: "Requires Node.js and a package manager."
capabilities: programming-nodejs svelte
domains: developer
rules:
- file(svelte.config.js)
- file(svelte.config.ts)
- grep(svelte, package.json)
- content(svelte)
---
- ## Mental model
-
- Svelte 5 compiles components to small, efficient updates; SvelteKit adds routing, server-side rendering, and form actions on top. The Svelte advantage is its compiler — most apps need very little client state management, no global store libraries, and no `useEffect`-style synchronization. Most maintenance trouble comes from importing React habits: pushing everything client-side, manual derived state, and effects doing the job of derivations.
-
- ## Runes (Svelte 5)
+ ## Overview
- - `$state(...)` for reactive local state — replaces top-level `let` reactivity
- - `$derived(...)` for computed values — replaces the old `$:` reactive blocks; deep dependency tracking is automatic
- - `$effect(...)` for side effects only (DOM, subscriptions, non-Svelte libraries) — never for derived values
- - `$props()` for typed component props; `$bindable()` for two-way bindable props
- - Runes work in `.svelte.ts` / `.svelte.js` files too — extract reactive logic into modules
- - Avoid effects that read state and write state — they create loops and obscure data flow
+ Write Svelte 5 components and SvelteKit applications with explicit reactive ownership and request-safe server boundaries. Research baseline: 2026-09-05, Svelte 5 and SvelteKit 2 stable; SvelteKit 3 is a release candidate. Read package versions, adapter, compiler options, and existing component mode first. Do not mix prerelease migration instructions into stable maintenance work.
- ## Component design
+ ## Mental model
- - One component per `.svelte` file; `<script lang="ts">` → markup → `<style>` ordering
- - Props are an API — type them with `$props<{...}>()` and document defaults
- - Snippets (Svelte 5) replace slots for reusable markup with parameters
- - Keep components small; extract sub-components when sections have their own state or events
- - Scoped styles by default — global styles only via `:global(...)` when intentional
+ Runes express state and derivation; effects synchronize with external systems after rendering. SvelteKit separates request-local server work from code that also executes in the browser. Reactive module state is not automatically isolated between server requests.
- ## SvelteKit architecture
+ ## Runes and component contracts
- - File-based routing in `src/routes/`: `+page.svelte` (page), `+layout.svelte` (shared layout), `+page.ts` / `+page.server.ts` (load), `+server.ts` (API endpoints)
- - `+page.server.ts` runs only on the server — safe for database queries, secrets, server-only deps
- - `+page.ts` runs on both server and client (universal) — pure data fetching that can hydrate
- - `load` functions return data the page renders; throw `redirect()` / `error()` for control flow
- - Layouts compose; place shared data loading at the layout level and child pages inherit via `$page.data`
- - `hooks.server.ts` for cross-cutting concerns: auth, request logging, CSRF, response headers
+ - Use `$state` for owned reactive data and `$derived`/`$derived.by` for values calculated from it. Derivations should have no side effects.
+ - Dependencies are values read while the derivation/effect executes synchronously, including called functions; reads after `await` are not tracked by an ordinary effect. Conditional reads produce conditional dependencies.
+ - Destructuring a reactive object can take a nonreactive snapshot. Keep access through the object or derive the field when it must stay current. Destructuring `$props()` is compiler-supported and different.
+ - Use `$effect` for browser-side subscriptions and external APIs, with cleanup. Effects do not run during server rendering. Avoid updating state in an effect to calculate another value.
+ - Type `$props()` and define defaults only for truly optional props. Props belong to their owner; use callbacks for changes or explicit `$bindable` for a deliberate two-way API.
+ - Use event properties such as `onclick` and snippets with `{@render ...}` in new runes components. Preserve working legacy components when conversion is outside scope.
+ - Reusable reactive logic belongs in `.svelte.ts`/`.svelte.js`. Do not export directly reassigned rune state; expose operations/getters or a stable object. Stores remain supported and useful for explicit subscriptions and asynchronous streams.
+ - Use semantic controls, labels, and stable keys in `{#each items as item (item.id)}` when identity matters. `{#key}` destroys and recreates a subtree; it is not a routine refresh mechanism.
- ## Forms and mutations
+ ## Request isolation and navigation
- - Form actions (`+page.server.ts` `actions = { default: ... }`) for mutations — progressive-enhancement friendly, work without JS
- - `use:enhance` upgrades the form to a fetch-driven submission with optimistic updates
- - Validate on the server; share validation schemas (zod/valibot) between client and server when both validate
- - Return validation errors from actions; the page renders them via `form` prop
- - Use API endpoints (`+server.ts`) for non-form mutations, third-party integrations, or non-HTML responses
+ - Do not keep per-user state in a server module singleton, whether it uses runes, stores, or plain variables. Use request `locals`, returned load data, and component/context instances scoped to the rendered tree.
+ - Initialize shared UI context at the owning component. Svelte 5.40+ offers typed `createContext`; 5.57 adds its third presence-check function. Prefer the project's supported API instead of a module-global state shortcut.
+ - On Svelte 5 with Kit 2.12+, use `page` from `$app/state`. Derive values such as `$derived(page.params.id)`; legacy `$:` does not react to this API's updates.
+ - Pages/layouts can survive navigation. Recompute from changing props or page state instead of capturing the initial value once. Put shareable state in URL parameters and durable preferences in appropriate persistence.
- ## State management
+ ## Loading, mutations, and failures
- - Local reactive state with `$state` covers most needs — no global store library required for typical apps
- - Cross-component shared state: a module exporting a `$state` object, imported where needed
- - The `$page` store and SvelteKit's `goto`/`invalidate` cover URL-driven state and re-fetching
- - Persist state in URL search params, cookies, or localStorage — not in long-lived module-level reactive state across navigations
- - Avoid the legacy `writable`/`readable`/`derived` stores for new code unless interop demands them — runes replace them
+ - Use `+page.server.ts`/`+layout.server.ts` for secrets and database access. Universal `load` also runs in the browser; use its provided `fetch` and return data without mutating shared state.
+ - Put server-only helpers in `$lib/server` or server-only modules. Private environment imports belong behind this boundary. Authorize access where protected data and mutations are handled; a parent layout is not a universal authorization gate.
+ - Kit 2's `error(...)` and `redirect(...)` throw internally; call them directly and avoid broad catches that swallow them.
+ - Await data needed to decide status, redirects, or page structure. To stream noncritical server-load data, return its promise and render with `{#await}`; awaiting it before returning blocks streaming. Account for rejection handling and adapter support.
+ - Use form actions for ordinary form mutations. Return `fail(status, data)` for expected validation errors, preserve safe entered values, and validate/authorize on the server.
+ - `use:enhance` progressively enhances supported POST action forms; it does not automatically implement optimistic business state. When customizing it, preserve the intended default update/reset/invalidation behavior.
+ - Keep pending, empty, validation, and unexpected failure states distinct. Retry or default only when the domain permits it.
+ - In the researched stable documentation, remote functions and component async `await` require experimental opt-ins. Use them only when intentionally enabled and supported; ordinary load/actions remain valid.
- ## Server/client boundary
+ ## Example
- - Server-only code lives in `+page.server.ts`, `+server.ts`, `+layout.server.ts`, and `$lib/server/` — anything else may be bundled to the client
- - Secrets and server-only modules go behind the `$lib/server/` boundary; SvelteKit fails the build if they leak to client code
- - Environment variables: `$env/static/private` (server, build-time), `$env/static/public` (client-safe, build-time), `$env/dynamic/*` (runtime)
- - Stream data with `await streamed.foo` in load functions — page renders progressively without blocking on slow data
+ A typed prop and local state drive a derivation without an effect:
- ## Performance
+ ```svelte
+ <script lang="ts">
+ type Item = { id: string; title: string };
+ let { items }: { items: readonly Item[] } = $props();
+ let query = $state("");
+ let visible = $derived(
+ items.filter(item => item.title.includes(query))
+ );
+ </script>
- - Svelte's compiler already optimizes updates — micro-optimizations are rarely needed
- - Code-split happens at route boundaries automatically
- - Lazy-load heavy components with dynamic `import()` when they're conditionally rendered
- - Use `{#key value}{/key}` to force a subtree to recreate when identity changes
+ <label>
+ Filter items
+ <input bind:value={query} />
+ </label>
+ <ul>
+ {#each visible as item (item.id)}
+ <li>{item.title}</li>
+ {/each}
+ </ul>
+ ```
- ## Testing
+ ## Checklist
- - Vitest for unit and component logic
- - `@testing-library/svelte` for component tests — query by role and text, not by component internals
- - Playwright for end-to-end flows — particularly important for form actions and progressive enhancement paths
- - Mock at the network layer (MSW) rather than mocking `fetch`
+ - Confirm stable versus experimental APIs and supported TypeScript checker versions.
+ - Check derivation tracking, prop ownership, navigation reuse, and effect cleanup.
+ - Keep user data request-scoped and secrets out of universal modules.
+ - Verify form behavior with and without enhancement, failures, and streamed rejection paths.
+ - Use the project's installed `svelte-check`, lint, and focused tests when authorized; plain `tsc` does not validate Svelte templates. Report skipped execution.
- ## Project layout
+ ## References
- - `src/lib/` for reusable code (`$lib` alias); `src/lib/server/` for server-only modules
- - Feature folders inside `src/lib/` when the app grows; routes stay slim and import features
- - Components, types, and utilities colocated by feature, not split by kind
+ - [September 2026 release status](https://svelte.dev/blog/whats-new-in-svelte-september-2026)
+ - [Derived state](https://svelte.dev/docs/svelte/$derived) and [effect tracking](https://svelte.dev/docs/svelte/$effect)
+ - [State and request isolation](https://svelte.dev/docs/kit/state-management)
+ - [Reactive page state](https://svelte.dev/docs/kit/$app-state)
+ - [Load and streaming](https://svelte.dev/docs/kit/load)
+ - [Form actions and enhancement](https://svelte.dev/docs/kit/form-actions)
+ - [Remote function status](https://svelte.dev/docs/kit/remote-functions) and [async compiler status](https://svelte.dev/docs/svelte/await-expressions)