Overview
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.
Mental model
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.
Runes and component contracts
- 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.
Request isolation and navigation
- 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.
Loading, mutations, and failures
- 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.
Example
A typed prop and local state drive a derivation without an effect:
<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>
<label>
Filter items
<input bind:value={query} />
</label>
<ul>
{#each visible as item (item.id)}
<li>{item.title}</li>
{/each}
</ul>
Checklist
- 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.
References
1---2name: programming-svelte3description: Svelte 5 runes, SvelteKit architecture, server/client boundaries, and progressive enhancement. Auto-activates in Svelte projects.4license: Apache-2.05---67## Overview89Write 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.1011## Mental model1213Runes 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.1415## Runes and component contracts1617- Use `$state` for owned reactive data and `$derived`/`$derived.by` for values calculated from it. Derivations should have no side effects.18- 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.19- 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.20- 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.21- 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.22- Use event properties such as `onclick` and snippets with `{@render ...}` in new runes components. Preserve working legacy components when conversion is outside scope.23- 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.24- 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.2526## Request isolation and navigation2728- 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.29- 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.30- 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.31- 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.3233## Loading, mutations, and failures3435- 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.36- 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.37- Kit 2's `error(...)` and `redirect(...)` throw internally; call them directly and avoid broad catches that swallow them.38- 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.39- 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.40- `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.41- Keep pending, empty, validation, and unexpected failure states distinct. Retry or default only when the domain permits it.42- 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.4344## Example4546A typed prop and local state drive a derivation without an effect:4748```svelte49<script lang="ts">50 type Item = { id: string; title: string };51 let { items }: { items: readonly Item[] } = $props();52 let query = $state("");53 let visible = $derived(54 items.filter(item => item.title.includes(query))55 );56</script>5758<label>59 Filter items60 <input bind:value={query} />61</label>62<ul>63 {#each visible as item (item.id)}64 <li>{item.title}</li>65 {/each}66</ul>67```6869## Checklist7071- Confirm stable versus experimental APIs and supported TypeScript checker versions.72- Check derivation tracking, prop ownership, navigation reuse, and effect cleanup.73- Keep user data request-scoped and secrets out of universal modules.74- Verify form behavior with and without enhancement, failures, and streamed rejection paths.75- Use the project's installed `svelte-check`, lint, and focused tests when authorized; plain `tsc` does not validate Svelte templates. Report skipped execution.7677## References7879- [September 2026 release status](https://svelte.dev/blog/whats-new-in-svelte-september-2026)80- [Derived state](https://svelte.dev/docs/svelte/$derived) and [effect tracking](https://svelte.dev/docs/svelte/$effect)81- [State and request isolation](https://svelte.dev/docs/kit/state-management)82- [Reactive page state](https://svelte.dev/docs/kit/$app-state)83- [Load and streaming](https://svelte.dev/docs/kit/load)84- [Form actions and enhancement](https://svelte.dev/docs/kit/form-actions)85- [Remote function status](https://svelte.dev/docs/kit/remote-functions) and [async compiler status](https://svelte.dev/docs/svelte/await-expressions)