Default runes mode. SvelteKit, CSS, and state libraries follow the project; do not specify them in this skill.
Do not write APIs from training data. First determine 5.y, then read the ≤ current minor sections in the reference.
Determine version
- Read the installed
svelteversion (node_modules/svelte/package.jsonofversion, or the lockfile). Do not only look atpackage.jsonof^range. - Take 5.y(
5.56.9→5.56), open references/5.md, and use only the5.0…5.ysections in that file.
For newer 5.y not listed: base it on the highest covered section, then check the official docs / changelog. Do not use Svelte 4 / old syntax without runes.
Choose rune
Do not $state use it for everything. Anything that does not drive the template / $derived / $effect is a normal variable.
| API | Use for |
|---|---|
$state |
values that change and need to notify the UI. Objects/arrays default todeepproxy, suitable for in-place field changes (forms, drafts) |
$state.raw |
only whole replacement: large lists, API responses, immutable snapshots. Do not push / change fields; to change, assign the whole thing |
$derived |
values computed from other state (expressions, no side effects) |
$derived.by |
when the derived logic cannot fit in one expression |
$effect |
escape hatch. Synchronize external systems. Do not use it to assign to another $state as derivation |
$inspect |
debugging. Do not use $effect for logging |
| normal variable | does not drive the view, does not participate in derivation |
To track element fields in a list use $state([]); to update only when replacing the whole table, use $state.raw. When passing to structuredClone / non-proxy APIs use $state.snapshot。
$derived and $effect
Fixed structure, only embedding one reactive segment → reference directly, do not wrap $derived. Only use for a new filtered/sorted list, or a derivation to be reused, $derived。
let double = $derived(count * 2)
let total = $derived.by(() => numbers.reduce((a, n) => a + n, 0))
Do not:
$effect(() => {
double = count * 2
})
$effect does not run on the server. Do not wrap if (browser). Put user actions in event handlers or bind: . Integrating with DOM libraries such as D3: use {@attach}for 5.29+, only use:actionfor older. To observe external subscriptions use createSubscriber。
Components and data flow
- Do not let a single file grow huge (about 500 lines).
- props:
let { name, count = 0 } = $props(). For values depending on props use$derived, do not destructure and treat as constants. - Do not modify props that do not belong to you. Pass back with callback; when parent and child share the same object use
$bindable。 - Events:
onclick={...}, do noton:click。window/documentuse<svelte:window>/<svelte:document>, do notonMountinaddEventListener。 - Slots:
{#snippet}+{@render}, do not<slot>/$$slots。 {#each items as item (item.id)}, key with stable id, not index. Whenbind:into element fields, do not destructure item.- Cross-layer: context.
$statein a module will cross requests during SSR; do not share user state this way. - Child component styles: CSS variables (
<Child --color="red" />), do not randomly:global。 - class: clsx-style arrays/objects; do not write
class:directives in new code.
New code must not use: implicit let count = 0 reactivity,$: 、export let、$$props / $$restProps, or store as cross-component reactivity (use a class with a $state field),<svelte:component> / <svelte:self>。