This skill is a quick reference for the official Svelte module APIs, including svelte/store、svelte/action、svelte/transition、svelte/animate、svelte/easing、svelte/events、svelte/compiler and common compiler/runtime error codes.
When to use this skill
Use this skill when the user needs to look up the API signatures of specific Svelte modules, use the transition / in / out / animate directive, theuse: action、easing function, the meaning of error codes, or needs compiler configuration.
Critical: svelte/store
import { writable, readable, derived, readonly, get } from 'svelte/store';
| Function | Signature | Description |
|---|---|---|
writable |
(initial, start?) => { subscribe, set, update } |
Writable store |
readable |
(initial, start) => { subscribe } |
Read-only store (the value can only be set internally by start ) |
derived |
(stores, fn, initial?) => { subscribe } |
Derived store |
readonly |
(store) => store |
Returns a read-only version (no set/update) |
get |
(store) => value |
Get the value without subscribing (synchronous, creates a temporary subscription) |
Store Contract
store = {
subscribe: (subscription: (value: any) => void) => (() => void),
set?: (value: any) => void
}
When to Use Stores vs Runes
- Prefer runes (
$statein.svelte.jsfiles) for shared state and logic extraction. - Use stores when you have complex async data streams, need manual control over updates, or want RxJS-style operators.
writable Start Function
writable(initial, start) — the start callback is called when the subscriber count goes 0 → 1 (not 1 → 2). Return a stop function that runs when subscribers go 1 → 0. The start callback also receives an update function (in addition to set) for transform-based updates.
derived Forms
// single store
const doubled = derived(a, ($a) => $a * 2);
// array of stores
const sum = derived([a, b], ([$a, $b]) => $a + $b);
// async — second arg is `set`, third is `update`, third `derived` arg is initial value
const delayed = derived(a, ($a, set) => {
setTimeout(() => set($a), 1000);
}, 0);
// with cleanup — return a function from the callback
const tick = derived(frequency, ($frequency, set) => {
const id = setInterval(() => set(Date.now()), 1000 / $frequency);
return () => clearInterval(id);
}, 0);
get(store) Caveat
Creates a temporary subscription, reads, then unsubscribes. Avoid in hot paths.
Critical: svelte/action (use: directive)
Actions are functions called when an element is mounted, added with the use: directive:
<script>
/** @type {import('svelte/action').Action} */
function myAction(node) {
// node: HTMLElement — runs once on mount (client only, not SSR)
$effect(() => {
// setup
return () => {
// teardown — runs on unmount
};
});
}
</script>
<div use:myAction>...</div>
[!NOTE] In Svelte 5.29 and newer, consider using
{@attach ...}instead — more flexible and composable.
Action With Parameter
The parameter is not reactive — the action runs once on mount. Use $effect inside to react to parameter changes.
<script>
/** @type {import('svelte/action').Action<HTMLInputElement, number>} */
function debounce(node, delay) {
$effect(() => {
const handler = () => setTimeout(() => node.dispatchEvent(new Event('done')), delay);
node.addEventListener('input', handler);
return () => node.removeEventListener('input', handler);
});
}
</script>
<input use:debounce={300} />
Legacy update/destroy Form
<script>
/** @type {import('svelte/action').Action<HTMLDivElement, string>} */
function colorize(node, color) {
node.style.color = color;
return {
update(newColor) { node.style.color = newColor; },
destroy() { node.style.color = ''; }
};
}
</script>
<div use:colorize={'red'}>...</div>
Prefer the $effect form for new code.
Typing — Action<Node, Parameter, Events>
<script lang="ts">
import type { Action } from 'svelte/action';
const gestures: Action<HTMLDivElement, undefined, {
onswipeleft: (e: CustomEvent) => void;
onswiperight: (e: CustomEvent) => void;
}> = (node) => {
// dispatch: node.dispatchEvent(new CustomEvent('swipeleft'));
};
</script>
<div use:gestures onswipeleft={next} onswiperight={prev}>...</div>
The third type parameter makes onswipeleft / onswiperight type-check in the template.
Critical: svelte/transition (transition: directive)
<script>
import { fade, fly, slide, scale, blur, draw, crossfade } from 'svelte/transition';
</script>
{#if visible}
<div transition:fade={{ duration: 300, delay: 100 }}>fade</div>
<div transition:fly={{ y: 20, duration: 300 }}>fly</div>
<div transition:slide>slide</div>
<div transition:scale={{ start: 0.5, duration: 300 }}>scale</div>
<div transition:blur={{ amount: 10 }}>blur</div>
{/if}
transition: is bidirectional — reverses on interrupt.
Built-in Transitions
| Function | Effect | Key parameters |
|---|---|---|
fade |
Opacity | duration |
fly |
Fly in from a position | x, y, opacity |
slide |
Slide | axis: 'x' | 'y' |
scale |
Scale | start, opacity |
blur |
Blur + opacity | amount |
draw |
SVG path drawing | speed or duration |
crossfade |
Paired crossfade | duration, fallback |
Transition Parameters
{
delay?: number, // ms before starting (default 0)
duration?: number, // ms (default 400)
easing?: function, // easing (default linear)
css?: (t, u) => string,// web animation CSS (preferred)
tick?: (t, u) => void // imperative (use only when CSS cannot)
}
t runs 0 → 1 on intro, 1 → 0 on outro. u = 1 - t. Easing is applied to t before it reaches css / tick. css and tick are mutually exclusive.
Local vs Global
<!-- local: only animates when this block is created/destroyed -->
<div transition:fade>...</div>
<!-- global: animates whenever ANY ancestor block toggles -->
<div transition:fade|global>...</div>
Transition Events
| Event | Fires |
|---|---|
introstart |
before intro animation |
introend |
after intro animation |
outrostart |
before outro animation |
outroend |
after outro animation |
<div
transition:fly={{ y: 200, duration: 2000 }}
onintrostart={() => (status = 'intro started')}
onoutroend={() => (status = 'outro ended')}
>...</div>
Custom Transition Function
// @noErrors
transition = (
node: HTMLElement,
params: any,
options: { direction: 'in' | 'out' | 'both' }
) => {
delay?: number;
duration?: number;
easing?: (t: number) => number;
css?: (t: number, u: number) => string;
tick?: (t: number, u: number) => void;
}
If the function returns a function (instead of an object), Svelte calls it in the next microtask — this is what crossfade uses to coordinate paired transitions.
Critical: in: and out: (separate transitions)
in: and out: are unidirectional — they do not reverse on interrupt. If you toggle a block off mid-intro, the in-flight intro is abandoned and the out-transition restarts from t=0.
<script>
import { fade, fly } from 'svelte/transition';
let visible = $state(false);
</script>
{#if visible}
<!-- flies in, fades out — NOT a reversible fly/fade -->
<div in:fly={{ y: 200 }} out:fade>...</div>
{/if}
| Directive | When it runs | Reversible on interrupt? |
|---|---|---|
transition: |
enter or leave | yes (reverses mid-flight) |
in: |
enter only | no (abandoned) |
out: |
leave only | no (always plays forward) |
Use in: / out: when enter and exit animations should be visually different. The third argument to a custom transition function is { direction: 'in' | 'out' | 'both' }.
Critical: svelte/easing
import { cubicIn, cubicOut, cubicInOut, elasticOut, bounceOut } from 'svelte/easing';
| Easing | Description |
|---|---|
linear |
Constant speed |
quadIn/quadOut/quadInOut |
Quadratic |
cubicIn/cubicOut/cubicInOut |
Cubic easing |
quartIn/quartOut/quartInOut |
Quartic |
quintIn/quintOut/quintInOut |
Quintic |
sineIn/sineOut/sineInOut |
Sine |
expoIn/expoOut/expoInOut |
Exponential |
circIn/circOut/circInOut |
Circular |
elasticIn/elasticOut/elasticInOut |
Elastic |
backIn/backOut/backInOut |
Back |
bounceIn/bounceOut/bounceInOut |
Bounce |
<div transition:fly={{ y: 100, easing: cubicOut }}>...</div>
Critical: animate: (svelte/animate)
animate: runs when the index of an existing item changes inside a keyed {#each} block — not on add/remove. It must be on the immediate child of a keyed each block.
<script>
import { flip } from 'svelte/animate';
let items = $state(['apple', 'banana', 'cherry']);
</script>
{#each items as item (item)}
<li animate:flip={{ duration: 400, easing: quintOut }}>{item}</li>
{/each}
flip (FLIP technique)
Measures from and to bounding rects, applies an inverse transform, then animates back to identity. See references/animations-reference.md for full API.
flip(node, {
delay?: number,
duration?: number | (node, { from, to }) => number,
easing?: function
})
Custom Animation Function
// @noErrors
animation = (
node: HTMLElement,
{ from: DOMRect, to: DOMRect },
params: any
) => {
delay?: number;
duration?: number;
easing?: (t: number) => number;
css?: (t: number, u: number) => string;
tick?: (t: number, u: number) => void;
}
from and to are the element's bounding rects before and after the reorder. Compute distance, angle, etc. to drive custom effects:
<script>
import { cubicOut } from 'svelte/easing';
function whizz(node, { from, to }) {
const dx = from.left - to.left;
const dy = from.top - to.top;
const d = Math.sqrt(dx * dx + dy * dy);
return {
duration: Math.sqrt(d) * 120,
easing: cubicOut,
css: (t, u) => `transform: translate(${u * dx}px, ${u * dy}px) rotate(${t * 360}deg);`
};
}
</script>
{#each list as item (item)}
<div animate:whizz>{item}</div>
{/each}
animate: does not run on add/remove — combine with in: / out: for those.
Critical: svelte/events
Provides the on function, replacing addEventListener(correctly handles event delegation and stopPropagation):
<script>
import { on } from 'svelte/events';
const handler = on(node, 'click', (event) => {
// 正确处理委托和 stopPropagation
}, { passive: true });
</script>
Critical: svelte/reactivity
$effect.tracking
$effect.tracking(); // boolean,是否在追踪上下文中
createSubscriber
Create a subscriber (only works in a tracking context):
import { createSubscriber } from 'svelte/reactivity';
const subscriber = createSubscriber((track) => {
track(value); // 声明依赖
});
Critical: Compiler Errors (common)
| Code | Meaning | Solution |
|---|---|---|
2304 |
Undefined reference | Check variable name spelling and imports |
2322 |
Target element does not exist | Check target selector |
2554 |
Type mismatch | Check TypeScript type annotations |
2451 |
Modifying state outside an effect | Do not assign directly in $effect in $state |
7006 |
Incorrect argument type | Check the function signature |
Critical: Runtime Warnings (common)
| Code | Meaning | Solution |
|---|---|---|
ownership_invalid_mutation |
Modified non-own state | Do not modify a prop directly; use $bindable |
await_waterfall |
await dependency chain causes performance issues | Refactor the data-fetching logic |
state_teardown |
Attempted to update after destruction | Ensure the component is not unmounted before accessing |
Critical: svelte/compiler
Configuration options
import { compile } from 'svelte/compiler';
const result = compile(source, {
filename: 'App.svelte',
generate: 'dom', // 'dom' | 'ssr' | false
name: 'Component', // 导出名
format: 'esm', // 'esm' | 'cjs'
sveltePath: 'svelte', // svelte 包路径
dev: true, // 开发模式(添加检查)
css: 'injected', // CSS 注入方式
runes: false, // 强制 Legacy Mode
// ...
});
compile output
result.js.code; // 编译后的 JS
result.css.code; // 编译后的 CSS(如有)
result.warnings; // 警告列表
result.stats; // 编译统计
Quick Fixes
| Problem | Solution |
|---|---|
| Transition does not trigger | Confirm the element is destroyed/recreated when the conditional block changes; use {#key} |
in:/out: switching midway behaves strangely |
This is the expected behavior of unidirectional — only transition: is reversible |
animate:flip does not work |
There must be (key) and animate: must be on a direct child element of the keyed eachdirect child elementon |
flip Animation is not smooth |
Ensure the key is unique and stable |
| Easing function does not work | Confirm that it is correctly imported from svelte/easing correctly imported |
use: Parameter changes do not take effect |
action is called only once; use $effect to read parameters internally |
| Compiler error | See the error code table above |
| Runtime warning | Check the ownership chain |
Examples
Practical usage patterns with detailed explanations:
- examples/README.md - Entry point
- examples/store-patterns.md - Writable, readable, derived stores, auto-subscribe, cross-component sharing
- examples/motion.md - Tweened and spring animations with easing
- examples/transition.md - Built-in transitions, custom transitions, in/out transitions
- examples/transitions-advanced.md - Crossfade, deferred transitions, transition events, custom css/tick
- examples/in-out-animate.md - in:/out: separate transitions, animate:flip, custom animation functions
- examples/use-action.md - use: directive with typing, lifecycle, custom events
References
Complete API documentation:
- references/README.md - Entry point
- references/store-reference.md - Store API signatures: writable, readable, derived, get, readonly
- references/motion-reference.md - Motion API: tweened, spring, easing functions
- references/transition-reference.md - Transition API: fade, fly, slide, scale, custom transitions
- references/transitions-complete.md - Complete reference for all built-in transitions, events, |global, custom functions
- references/in-out-reference.md - in: / out: separate (unidirectional) transitions
- references/animations-reference.md - animate: directive, flip, custom animation functions
- references/use-action-reference.md - use: action typing, lifecycle, Action<>, custom events
Gotchas
svelte/transitionofcssandtickare mutually exclusive — only one of the two can be usedin:/out:is irreversible — on interruption the in-flight intro is abandoned and the outro restarts from t=0animate:must be on a direct child element of the keyed each — animate: on a nested div will not runfliprequires a stable key — use a unique ID instead of the array indexanimate:does not respond to add/remove — it only triggers on reorder- Store subscribe returns an unsubscribe function — automatically unsubscribes when the component unmounts
get(store)creates a temporary subscription — frequent calls affect performanceelasticIn/elasticOutis slower — heavy use may affect performanceuse:actionparameter changes do not trigger a re-call — use$effectto respond inside the action