Svelte Cheatsheet - Svelte Framework Reference

For frontend developers who want less boilerplate and a minimal runtime. Svelte compiles reactivity directly into native DOM updates at build time, so the runtime is tiny and there is no virtual-DOM mental model; but it lives on assignment-driven updates, $: recomputation, and Store subscriptions that take getting used to. By the end you can declare reactive state with let and $:, communicate between components via events and dispatch, share cross-component state with Store, and hook into lifecycle and transitions.

Web Services·50 commands·Last updated 2026-07-21
svelteFrontendFrameworkComponents

Typical Use Case

For developers moving over from React or Vue, or working on projects where component weight is a real concern. The sheet follows the three main lines of Svelte: template block syntax handles structure, assignment-driven reactivity handles data, and actions plus transitions handle interaction. Above that sit lifecycle hooks for side effects, tick for waiting on DOM updates, and stores for pulling state out of components so it can be shared and torn down cleanly.

Template Syntax 6

{ expression }
Insert JavaScript expression in template
{@html rawHtmlString}
Render raw HTML (beware of XSS)
{#if condition}...{:else if}...{:else}...{/if}
Conditional rendering
{#each items as item, index}...{/each}
Iterate over arrays
{#await promise}...{:then value}...{:catch error}...{/await}
Handle async Promise states
{#key expression}...{/key}
Destroy and recreate content when expression changes

Reactive Declarations 5

let count = 0;
Declare a variable, assignment triggers view update
$: doubled = count * 2;
Reactive declaration, auto-recalculates on dependency changes
$: { console.log(count); }
Reactive statement block, executes when dependencies change
arr = [...arr, newItem];
Array update: assign new reference to trigger reactivity
obj = { ...obj, key: value };
Object update: assign new reference to trigger reactivity

Components & Props 7

export let name;
Declare a component prop
export let name = "default";
Prop with default value
$$props
Access all passed props
$$restProps
Access undeclared remaining props
<slot />
Default slot for child content
<slot name="header" />
Named slot
<slot let:item={item} />
Scoped slot, passing data to parent

Events & Bindings 7

<button on:click={handler}>
Bind DOM event
<form on:submit|preventDefault={handler}>
Event modifiers (preventDefault/stopPropagation)
import { createEventDispatcher } from "svelte"; const dispatch = createEventDispatcher(); dispatch("event", data);
Dispatch custom events from child to parent
<input bind:value={name}>
Two-way binding of input value
<input bind:this={inputEl}>
Bind DOM element reference
<input type="checkbox" bind:group={selected}>
Checkbox group binding to array
<input type="radio" bind:group={choice}>
Radio group binding

Lifecycle 6

import { onMount } from "svelte"; onMount(() => { ... });
Execute after component mounts (good for API calls)
import { beforeUpdate } from "svelte"; beforeUpdate(() => { ... });
Execute before DOM update
import { afterUpdate } from "svelte"; afterUpdate(() => { ... });
Execute after DOM update
import { onDestroy } from "svelte"; onDestroy(() => { ... });
Execute before component unmounts (cleanup timers/subscriptions)
import { tick } from "svelte"; await tick();
Wait for DOM to finish updating, get updated DOM state
import { untrack } from "svelte"; untrack(() => { ... });
Temporarily stop dependency tracking in reactive declarations

Store 7

import { writable } from "svelte/store"; export const count = writable(0);
Create a writable store
import { readable } from "svelte/store"; export const time = readable(new Date(), (set) => { ... });
Create a readable store (with initial value and set function)
import { derived } from "svelte/store"; export const doubled = derived(count, ($c) => $c * 2);
Create a derived store that depends on other stores
$count
Auto-subscribe and unsubscribe in components
count.set(10);
Directly set a store value
count.update(n => n + 1);
Update store value based on current value
import { get } from "svelte/store"; const val = get(count);
Get store value outside component context

Actions & Transitions 7

use:action={{ param }}
Custom action to manipulate DOM elements
transition:fade
Fade in/out transition
transition:slide
Slide expand/collapse transition
transition:scale
Scale transition
in:fly={{ x: 200, duration: 500 }}
Enter animation (fly in)
out:fly={{ x: -200 }}
Leave animation (fly out)
animate:flip
Animate list item position changes (FLIP)

Advanced Features 5

import { setContext, getContext } from "svelte"; setContext("key", value); const val = getContext("key");
Cross-level component communication (Context API)
import { hasContext } from "svelte"; hasContext("key");
Check if a context key exists
<script context="module">...</script>
Module-level script, runs once per component definition
export const greet = (name) => `Hello ${name}`;
Export functions from module script for external use
svelte:options
Compiler options, e.g. <svelte:options immutable={true} />

Parameter matrix

ParameterEffectExample
{ expression }模板中插入 JavaScript 表达式<h1>{user.name}</h1>
{@html raw}渲染原始 HTML 字符串,存在 XSS 风险{@html post.bodyHtml}
{#if}...{:else if}...{:else}条件渲染分支{#if loading}<Spinner />{:else}<List />{/if}
{#each items as item, i}遍历数组渲染,第二项为下标{#each todos as todo, i}{i}. {todo.text}{/each}
{#await promise}按 pending/then/catch 三态渲染异步结果{#await loadUser()}<Spinner />{:then u}<Profile {u} />{/await}
{#key expression}表达式变化时销毁并重建内部内容{#key activeTab}<Panel {tab} />{/key}
$: doubled = count * 2响应式声明,依赖变化时自动重算$: total = price * qty
arr = [...arr, x]数组更新必须赋新引用才触发更新items = [...items, newItem]
obj = { ...obj, k: v }对象更新同样要整体赋新引用state = { ...state, open: true }
export let name声明组件接收的 propexport let name;
$$restProps读取未显式声明的其余 props<input {...$$restProps} />
<slot name="header" />具名插槽,供父组件填充指定区域<slot name="header" />
<slot let:item={item} />作用域插槽,把子组件数据回传父组件<slot let:item={item}>{item.label}</slot>
on:click|preventDefault绑定 DOM 事件并使用事件修饰符<form on:submit|preventDefault={save}>
createEventDispatcher()子组件向父组件派发自定义事件dispatch('change', data)
bind:value / bind:this输入值双向绑定 / 绑定 DOM 元素引用<input bind:value={name} bind:this={inputEl} />
bind:group复选框组绑定到数组、单选框组绑定到变量<input type="checkbox" bind:group={selected} value={id} />
onMount / onDestroy挂载后执行请求 / 销毁前清理定时器与订阅onDestroy(() => clearInterval(timer))
await tick()等待 DOM 更新完成后再读取元素状态await tick(); el.focus();
untrack(() => ...)在响应式声明中临时取消依赖追踪untrack(() => log(count))
writable / readable / derived创建可写、只读、派生三种 Storeconst doubled = derived(count, (n) => n * 2)
get(store)在非组件上下文同步读取 Store 当前值const val = get(count)
use:action自定义动作函数操作 DOM 元素use:tooltip
transition:fade / slide / scale元素出现与消失时播放过渡动画transition:slide
in:fly={{ x: 200 }}进入动画(飞入),out: 对应离开动画in:fly={{ x: 200, duration: 500 }}
animate:flip列表元素位置变化时补间动画(FLIP)animate:flip={{ duration: 200 }}
setContext / getContext跨层级组件通信,hasContext 判断是否存在setContext('theme', 'dark')
svelte:options编译器选项,如 immutable 关闭深度代理<svelte:options immutable={true} />
context='module'模块级脚本,每个组件实例只执行一次<script context="module">

Common pitfalls

Symptompush 了数组元素、设置了对象属性,界面却不更新

CauseSvelte 的响应式基于赋值追踪,push 与 obj.x = v 这类原地修改不会触发重新赋值

Fix改用新引用写法:arr = [...arr, newItem]、obj = { ...obj, key: value }

Symptom异步数据回来后 DOM 还没更新就去读 input.value,结果拿到旧值

Cause赋值只排入了更新队列,DOM 渲染在下一个微任务才发生

Fix在赋值后 await tick(),等 DOM 更新完成再读取元素状态

Symptom组件销毁后仍在打接口,控制台刷请求或出现 setState 警告

Cause在 onMount 里发起的请求没有随组件销毁取消

Fix把 AbortController 或取消句柄存在变量里,在 onDestroy 里取消请求并清理定时器

Symptom响应式声明里只想读一次值,却被无关变量变化触发重算

Cause$: 块会自动收集块内所有读取的依赖,包含非响应式来源的读取

Fix把纯读取放进 untrack(() => ...),让它不参与依赖追踪

Symptom切换列表项状态后,动画错乱或复用了上一次的 DOM

Cause列表里缺少 key 语义,Svelte 按位置复用元素

Fix用 {#key expression} 包住需要重建的内容,或调整 {#each} 的来源结构

Symptom{@html} 渲染用户提交内容后出现脚本被执行

Cause{@html} 直接把字符串当 HTML 解析输出,不做转义

Fix对用户输入先做净化,或改用普通 { expression } 由 Svelte 自动转义

Symptom在组件外部或事件回调里读 Store 拿到 undefined

Cause$store 自动订阅语法只在组件顶层可用,脱离组件后没有订阅者

Fix在非组件上下文用 get(store) 同步取值,或改用 store.subscribe() 手动订阅

Troubleshooting

  1. 1确认某个状态是否真的触发了重新渲染

    $: console.log(count);

    在响应式语句块里打印依赖变量,控制台出现新输出说明赋值已被追踪到。

  2. 2确认数组/对象更新是否走了新引用

    arr = [...arr, newItem];

    把原地 push 改成整体赋值;若界面随即更新,问题就出在原来那行。

  3. 3在 DOM 更新后立即聚焦或测量元素

    await tick();

    tick() 之后的 DOM 已是最新状态,适合 focus()、getBoundingClientRect() 等操作。

  4. 4把不该被追踪的读取排除出响应式声明

    untrack(() => render(count));

    日志里若看到 render 因无关变量被重复调用,就把该读取包进 untrack。

  5. 5强制重建一段内容而不是复用

    {#key activeId}<Panel data={activeId} />{/key}

    内容切到别的数据源后仍显示旧内容时,用 {#key} 显式声明重建边界。

  6. 6在组件之外读取 Store 的当前值

    const val = get(count);

    get() 同步读出当前值,适用于事件回调、定时器、模块顶层等无组件上下文处。

  7. 7给子组件派发事件并在父组件接收

    dispatch('change', data);

    父组件用 on:change={handler} 接收,detail 里就是 dispatch 传出的 data。

Command Examples

Render an indexed list with each and the index

{#each items as item, index} <li>{index + 1}. {item}</li> {/each}

index is the second binding of {#each} and can be used directly in markup. Supply a stable key with the (item.id) form; keying on the object itself rebuilds the whole block whenever the data is recreated, which is slower, not faster.

The await block covers loading, success, and failure states

{#await promise} <p>加载中…</p> {:then user} <p>{user.name}</p> {:catch error} <p>{error.message}</p> {/await}

If you write only {#await promise}…{/await} with no :then and :catch, a rejected promise shows no feedback at all — the UI just keeps looking like it is loading. Writing all three branches is the cheapest way to avoid silent failure.

Use get() outside components instead of the store-value sugar

export const count = writable(0); count.update((n) => n + 1); // 组件外读值: import { get } from "svelte/store"; const val = get(count);

$count is compile-time sugar and only works inside a component or .svelte file. Module-level event handlers and utility functions must use get(). Writing $count there is a compile error whose stack often points at a distant, seemingly unrelated file.

Fetch on mount, clean up on destroy

import { onMount, onDestroy } from "svelte"; let timer; onMount(() => { timer = setInterval(refresh, 5000); }); onDestroy(() => { clearInterval(timer); });

onMount runs on every mount, and conditional rendering unmounts the component while the timer, subscription, or socket keeps running. Teardown belongs in onDestroy; otherwise toggling the component in and out of the page stacks up duplicate timers.

derived computes from an existing store instead of duplicating state

export const doubled = derived(count, (value) => value * 2);

A common mistake is copying a derived value into a component-local variable, which creates two sources of truth. derived makes it a store: it recomputes automatically and every subscriber sees the same value.

Common Pitfalls

  • {@html rawHtmlString} injects raw markup, so anything from a user or an API is an open XSS hole. Use a plain interpolation unless you truly know the content is trusted.
  • Assigning to a child component's export let prop silently mutates the parent's state. Props are a one-way source; use events or bind to change state across a boundary.
  • A $: reactive statement only tracks variables that appear directly in it. Hide the value inside an object or pass it through a function and the dependency is never registered, so the UI stops updating.
  • A request started in onMount with no check for an unmounted component throws or leaks when the response lands. Guard it with a flag in the returned cleanup function.
  • {#key} destroys and rebuilds everything inside it every time the expression changes. Placed outside an #each, or bound to something that changes often, it rebuilds whole DOM subtrees repeatedly.
  • Outside SSR-safe paths, code touching window or document crashes on the server. Guard the type check, or access them only after mount.

Tips

  • Svelte is a compiled framework with no runtime, producing minimal code
  • Reactivity is triggered by assignment; arrays and objects need new assignments
  • $: reactive declarations track any dependency changes
  • Store manages state outside components, $store syntax auto-subscribes
  • Svelte 5 introduces Runes ($state/$derived/$effect) new reactivity system

FAQ

Why does the view not update when I push an item onto an array?

Svelte reactivity is assignment-driven, so arrays and objects must be replaced with a new reference to trigger an update. Use arr = [...arr, newItem] to append and obj = { ...obj, key: value } to change a field, never arr.push(x) or obj.k = v, which mutate in place. With Svelte 5 you can wrap the value in a $state rune for an explicitly reactive container, which follows the same replace-the-reference rule. Change those two spots and the template re-renders.

How does a $: reactive statement differ from a plain computed variable?

$: doubled = count * 2; is reactive: the compiler inspects the right-hand side, records which variables it depends on, and recomputes automatically when count changes — the same applies to a statement block such as $: { console.log(count); }. A plain const or let is evaluated once at declaration and never reruns. Reactive declarations must live at the top level of the component script and are ordered by their dependency graph. The common trap is a variable introduced inside the block becoming a dependency itself and looping; when that happens, wrap the part in question with untrack(() => { ... }) to stop dependency tracking.

When should shared state live in a Store instead of a component variable?

Reach for a store when state is shared across components or should be managed outside the component tree. Create one with import { writable } from "svelte/store", read it in components as $count — the subscription is automatic and torn down on destroy — and write with count.set(10) or count.update(n => n + 1) for an update based on the current value. Use readable for write-protected state and derived(count, ($c) => $c * 2) for values computed from other stores. Outside a component the $ prefix does not exist, so read the value with get(count).

How do slots and props divide the job of passing content into a child component?

Structured values travel as props: export let name declares what the component receives, export let name = "default" supplies a default, $$props exposes everything passed in and $$restProps only the ones you did not declare. Template content travels through <slot />, and when a child needs several insertion points use named slots such as <slot name="header" />. When the child needs to hand data back up to the parent, use a scoped slot: <slot let:item={item} />.

When do I need a lifecycle hook instead of just calling a function in the component?

Anything touching the DOM or a browser resource belongs in a hook. onMount(() => { ... }) runs after the component mounts, which is where an API request belongs; onDestroy(() => { ... }) runs before teardown and is where you clear timers and unsubscribe, preventing leaks; beforeUpdate and afterUpdate fire either side of a DOM update. await tick() lets you wait until the DOM has actually updated, e.g. to measure an input right after changing it. Pure computation needs none of this — a $: declaration is enough.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us