⏳
Loading cheatsheet...
Reactive declarations, components, stores, animations, transitions, SvelteKit routing, and server-side rendering.
<!-- ── Basic Reactivity ── -->
<script>
// Variables are reactive by default!
let count = 0;
let name = 'Alice';
function increment() {
count += 1; // triggers re-render
}
// Arrays and objects need assignment for reactivity
let items = ['a', 'b', 'c'];
function addItem() {
items = [...items, 'new']; // reassignment triggers update
// items.push('new') does NOT trigger update!
}
// Object reactivity
let user = { name: 'Alice', age: 30 };
function updateName() {
user = { ...user, name: 'Bob' }; // spread creates new object
// user.name = 'Bob' works for top-level props in Svelte 5
}
// $: reactive declaration (derived state)
$: doubled = count * 2;
// $: reactive statement (side effects)
$: if (count > 10) {
console.log('Count is high!');
}
// $: reactive with multiple dependencies
$: {
console.log(count, name);
// cleanup (runs when dependencies change)
return () => console.log('cleaned up');
}
</script>
<button on:click={increment}>Count: {count}</button>
<p>Doubled: {doubled}</p>
<p>Name: {name}</p>
<input bind:value={name} />| Works | Does NOT Work |
|---|---|
| count += 1 | arr.push(item) |
| obj = { ...obj, key: val } | obj.key = val (Svelte 4) |
| arr = [...arr, item] | arr[index] = val |
| arr.filter(x => x) | arr.sort() |
| Rune | Description |
|---|---|
| $state | Fine-grained reactive signal |
| $derived | Computed from other signals |
| $effect | Side effect on change |
| $props | Component prop declaration |
| $bindable | Two-way bindable prop |
push(), splice(), sort() mutate in place — use reassignment instead: arr = [...arr, new] or spread into new array.<!-- ── Child Component ── -->
<script>
// Props (Svelte 4)
export let title = 'Default';
export let count = 0;
// Svelte 5: $props rune
// let { title, count = 0 } = $props();
// Events (callback props)
export let onIncrement: () => void;
// React to prop changes
$: console.log('count changed:', count);
function handleClick() {
count += 1;
onIncrement?.();
}
</script>
<h2>{title}</h2>
<p>Count: {count}</p>
<button on:click={handleClick}>+1</button>
<!-- Slot (default content) -->
<div class="card">
<slot>Default content</slot>
</div>
<!-- Named slot -->
<div class="layout">
<header><slot name="header" /></header>
<main><slot /></main>
<footer><slot name="footer" /></footer>
</div>
<!-- ── Parent Usage ── -->
<script>
import Counter from './Counter.svelte';
let total = 0;
</script>
<Counter
title="My Counter"
count={total}
onIncrement={() => total += 1}
>
<p>Slot content here!</p>
<span slot="header">Header content</span>
</Counter>| Pattern | Svelte |
|---|---|
| Props | export let name = "default" |
| Events | export let onEvent: (arg) => void |
| Slots | <slot /> and <slot name="x" /> |
| Forward events | <button on:click> dispatch |
| Context/Stores | setContext / getContext |
| Dynamic component | <svelte:component this={Comp} /> |
| Method | Description |
|---|---|
| createEventDispatcher | Svelte 5 typed events |
| on:click|forward | Forward all click events |
| on:click={(e) => ...} | Handle inline |
| import { createEventDispatcher } | Type-safe events |
<script>
let value = '';
let checked = false;
let selected = 'option1';
let num = 0;
let color = '#ff0000';
</script>
<!-- Bindings -->
<input bind:value={value} />
<input type="checkbox" bind:checked={checked} />
<input type="radio" group={selected} value="option1" />
<input type="number" bind:value={num} />
<input type="range" bind:value={num} min={0} max={100} />
<input type="color" bind:value={color} />
<select bind:value={selected}>
<option value="a">A</option>
<option value="b">B</option>
</select>
<textarea bind:value={value}></textarea>
<!-- Two-way binding with objects -->
<input bind:value={user.name} />
<!-- Group binding (checkboxes) -->
<script>
let flavors = [];
</script>
<label><input type="checkbox" bind:group={flavors} value="vanilla" /> Vanilla</label>
<label><input type="checkbox" bind:group={flavors} value="chocolate" /> Chocolate</label>
<!-- Event handlers -->
<button on:click={() => count += 1}>Click</button>
<button on:click={handleClick}>Click</button>
<button on:click={handleClick}>Click</button>
<!-- Event modifiers -->
<button on:click|preventDefault={handleSubmit}>Submit</button>
<button on:click|stopPropagation={handleClick}>Click</button>
<button on:keydown={(e) => e.key === 'Enter' && submit()}>Enter</button>
<!-- Dispatch custom event -->
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function save() {
dispatch('save', { data: value });
}
</script>
<!-- Lifecycle -->
<script>
import { onMount, onDestroy, beforeUpdate, afterUpdate, tick } from 'svelte';
onMount(() => {
console.log('mounted');
// fetch data, setup subscriptions
return () => console.log('cleanup');
});
onDestroy(() => { /* cleanup */ });
beforeUpdate(() => { /* before DOM update */ });
afterUpdate(() => { /* after DOM update */ });
async function updateAndScroll() {
count += 1;
await tick(); // wait for DOM to update
element.scrollIntoView();
}
</script>// ── Writable Store ──
import { writable, readable, derived, get } from 'svelte/store';
// Writable store
export const count = writable(0);
export const user = writable({ name: 'Alice', age: 30 });
// Update with function
count.update(n => n + 1);
// Set directly
count.set(10);
// Subscribe
const unsub = count.subscribe(value => console.log(value));
// ── Readable Store (derived) ──
export const doubled = derived(count, $count => $count * 2);
// Derived from multiple stores
export const fullName = derived(
[firstName, lastName],
([$first, $last]) => `$first $last`
);
// Custom readable store
export const time = readable(new Date(), (set) => {
const interval = setInterval(() => set(new Date()), 1000);
return () => clearInterval(interval);
});
// ── Auto-subscribe (in component) ──
<script>
import { count } from './store.js';
// Auto-subscribe: count changes will trigger re-render
$count
</script>
<p>Count: {$count}</p>
<button on:click={() => $count += 1}>+1</button>
// Store in Svelte 5 (Rune-based)
// import { _ } from 'svelte/internal/client';
// Not yet the primary pattern for storeswritable for mutable state, readable for derived/computed values, and derived for transformations. Auto-subscribe with $prefix in components.<script>
import { fade, fly, slide, scale, draw, flip }
from 'svelte/transition';
let visible = true;
</script>
<button on:click={() => visible = !visible}>Toggle</button>
<!-- Fade -->
{#if visible}
<p transition:fade>Fading content</p>
{/if}
<!-- Fly with parameters -->
{#if visible}
<p transition:fly={{ x: -200, duration: 500 }}>Flying in</p>
{/if}
<!-- Slide -->
{#if visible}
<p transition:slide>Sliding content</p>
{/if}
<!-- Scale -->
{#if visible}
<p transition:scale>Scaling content</p>
{/if}
<!-- Delay and duration -->
{#if visible}
<p transition:fade={{ duration: 300, delay: 100 }}>Delayed fade</p>
{/if}
<!-- In/out transitions -->
{#if visible}
<p in:fade out:fly={{ x: 200 }}>Custom in/out</p>
{/if}
<!-- Animate (for lists - FLIP animation) -->
<script>
let items = [1, 2, 3];
function shuffle() {
items = items.sort(() => Math.random() - 0.5);
}
</script>
{#each items as item (item)}
<div animate:flip>{item}</div>
{/each}
<button on:click={shuffle}>Shuffle</button>
<!-- Transition events -->
<p
transition:fly={{ duration: 300 }}
on:introstart={() => console.log('intro started')}
on:introend={() => console.log('intro ended')}
on:outrostart={() => console.log('outro started')}
on:outroend={() => console.log('outro ended')}
>
Content
</p>