<script> let count = $state(0); // count is reactive </script>
<button onclick={() => count++}> clicks: {count}
<script> let todos = $state([ { done: false, text: 'Learn Svelte' }, { done: false, text: 'Build an app' } ]); function toggle(index) { todos[index].done = !todos[index].done; // Reactivity applies to individual items } </script>
-
{#each todos as todo, index}
- toggle(index)} /> {todo.text} {/each}
<button onclick={() => todos.push({ done: false, text: 'New task' })}> Add Todo
<script> class Todo { done = $state(false); text = $state(''); constructor(text) { this.text = text; } reset = () => { this.text = ''; this.done = false; } } let todo = new Todo('Buy groceries'); </script>
{todo.text} - {todo.done ? 'Done' : 'Not done'}
todo.done = !todo.done}>Toggle todo.reset()}>Reset<script> let person = $state.raw({ name: 'Alice', age: 30 }); function updatePerson() { // This won't work because `person` is raw (not reactive) person.age += 1; // Instead, we must reassign the whole object person = { ...person, age: person.age + 1 }; } </script>
{person.name} is {person.age} years old.
Increase Age<script> let counter = $state({ count: 0 }); function logSnapshot() { console.log($state.snapshot(counter)); // Logs a plain object, not a Proxy } </script>
Count: {counter.count}
counter.count++}>Increment Log Snapshot<script> function add(getA, getB) { return () => getA() + getB(); } let a = $state(1); let b = $state(2); let total = add(() => a, () => b); // Pass getters to maintain reactivity </script>
{a} + {b} = {total()}
a++}>Increment A b++}>Increment B<script> let name = $state('Alice'); </script>
<input bind:value={ () => name, (v) => name = v.trimStart() } />
Hello, {name}!
<script lang="ts"> let count = $state(0); let doubled = $derived(count * 2); // `doubled` automatically updates when `count` changes </script>
<button onclick={() => count++}> Increment
{count} doubled is {doubled}
$derived(count * 2)
ensuresdoubled
always reflects the latestcount
.- You cannot modify state inside
$derived
, e.g.,count++
inside the expression.
<script lang="ts"> let numbers = $state([1, 2, 3]); // `total` is automatically recalculated when `numbers` change let total = $derived.by(() => { let sum = 0; for (const n of numbers) { sum += n; } return sum; }); </script>
<button onclick={() => numbers.push(numbers.length + 1)}> Add Number
{numbers.join(' + ')} = {total}
$derived.by(() => { ... })
is useful when the derived state requires loops or multiple steps.- The derived state (
total
) automatically updates whennumbers
change.
<script lang="ts"> let count = $state(0); let isEven = $derived(count % 2 === 0); </script>
<button onclick={() => count++}> Increment
{count} is {isEven ? 'Even' : 'Odd'}
$derived
tracks all synchronously accessed state inside its expression.- If
count
changes,isEven
will automatically update.
<script lang="ts"> let count = $state(0); let large = $derived(count > 10); </script>
<button onclick={() => count++}> Increment
Is count large? {large}
- Svelte only updates when
large
changes (false
→true
or vice versa). - If
count
changes within the same threshold (e.g.,5 → 6
), no update occurs.
<script> let size = $state(50); let color = $state('#ff3e00'); let canvas; $effect(() => { const context = canvas.getContext('2d'); context.clearRect(0, 0, canvas.width, canvas.height); // This effect runs whenever `color` or `size` changes context.fillStyle = color; context.fillRect(0, 0, size, size); }); </script>
<script> let count = $state(0); let milliseconds = $state(1000); $effect(() => { const interval = setInterval(() => { count += 1; }, milliseconds); // Cleanup function runs before effect re-runs or on component destroy return () => { clearInterval(interval); }; }); </script>
<button onclick={() => (milliseconds *= 2)}>Slower <button onclick={() => (milliseconds /= 2)}>Faster
<script> let size = $state(50); let color = $state('#ff3e00'); let canvas; $effect(() => { const context = canvas.getContext('2d'); context.clearRect(0, 0, canvas.width, canvas.height); // This effect re-runs when `color` changes but not when `size` changes context.fillStyle = color; setTimeout(() => { context.fillRect(0, 0, size, size); }, 0); }); </script>
<script> import { tick } from 'svelte'; let div = $state(); let messages = $state([]); $effect.pre(() => { if (!div) return; // Tracks `messages.length` so the effect re-runs when messages change messages.length; // Auto-scroll logic before the DOM updates if (div.offsetHeight + div.scrollTop > div.scrollHeight - 20) { tick().then(() => { div.scrollTo(0, div.scrollHeight); }); } }); </script>
{message}
{/each}<button onclick={() => messages = [...messages, 'New message']}>Add Message
<script> let count = $state(0); let doubled = $derived(count * 2); // Correct approach // Incorrect approach (causes infinite loops): // $effect(() => { // doubled = count * 2; // }); </script>
<button onclick={() => count += 1}>Increment
<script lang="ts"> let { value = $bindable(), ...props } = $props(); // `value` is bindable, allowing two-way data flow </script>
<input bind:value={value} {...props} />
<script lang="ts"> import FancyInput from './FancyInput.svelte'; let message = $state('hello'); // Using `$state` to create a reactive state variable </script>
{message}
<script lang="ts"> let { value = $bindable('fallback'), ...props } = $props(); // Default value set to 'fallback' </script>
<input bind:value={value} {...props} />
<script lang="ts"> import FancyInput from './FancyInput.svelte'; </script>
<script lang="ts"> let { value = $bindable('default text'), disabled = $bindable(false), ...props } = $props(); </script>
<input bind:value={value} bind:disabled={disabled} {...props} />
<script lang="ts"> import FancyInput from './FancyInput.svelte'; let message = $state('Editable text'); let isDisabled = $state(false); </script> isDisabled = !isDisabled}> Toggle Disabled
- The input's
value
anddisabled
states can both be bound to the parent. - The button toggles the
disabled
state dynamically. - Useful for forms and UI elements requiring controlled states.
<script lang="ts"> import MyComponent from './MyComponent.svelte'; </script>
<script lang="ts"> let props = $props(); </script>
This component is {props.adjective}
<script lang="ts"> let { adjective } = $props(); </script>
This component is {adjective}
<script lang="ts"> let { adjective = 'happy' } = $props(); </script>
This component is {adjective}
<script lang="ts"> let { super: trouper = 'lights are gonna find me' } = $props(); </script>
Song lyric: {trouper}
<script lang="ts"> let { a, b, c, ...others } = $props(); </script>
a: {a}, b: {b}, c: {c}
Other props: {JSON.stringify(others)}
<script lang="ts"> import Child from './Child.svelte'; let count = $state(0); </script>
<button onclick={() => count += 1}> clicks (parent): {count}
<Child {count} />
<script lang="ts"> let { count } = $props(); </script>
<button onclick={() => count += 1}> clicks (child): {count}
<script lang="ts"> import Child from './Child.svelte'; </script>
<Child object={{ count: 0 }} />
<script lang="ts"> let { object } = $props(); </script>
<button onclick={() => object.count += 1}> clicks: {object.count}
<script lang="ts"> import Child from './Child.svelte'; let object = $state({ count: 0 }); </script>
<Child {object} />
<script lang="ts"> let { object } = $props(); </script>
<button onclick={() => object.count += 1}> clicks: {object.count}
<script lang="ts"> const uid = $props.id(); </script> First Name:
<label for="{uid}-lastname">Last Name: </label>
<input id="{uid}-lastname" type="text" />
<svelte:options customElement="my-stepper" />
<script lang="ts"> function dispatch(type) { $host().dispatchEvent(new CustomEvent(type)); // $host() gives access to the custom element itself. // Dispatches 'increment' or 'decrement' events. } </script><button onclick={() => dispatch('decrement')}>decrement <button onclick={() => dispatch('increment')}>increment
<script lang="ts"> import './Stepper.svelte'; let count = $state(0); // State variable to track count </script>
<my-stepper ondecrement={() => count -= 1} onincrement={() => count += 1}
count: {count}
$host()
only works in components compiled as custom elements (<svelte:options customElement="..."/>
).- This approach allows dispatching custom events from a shadow DOM encapsulated component.
- Ensure the parent component listens to the dispatched events (
ondecrement
,onincrement
).
<script> let count = $state(0); let message = $state('hello'); $inspect(count, message); // Logs `count` and `message` whenever they change </script>
<button onclick={() => count++}>Increment
<script> let count = $state(0); $inspect(count).with((type, count) => { if (type === 'update') { console.log(`Count updated to: ${count}`); } }); </script>
<button onclick={() => count++}>Increment
<script> let message = $state('Hello'); $inspect(message).with(console.trace); </script>
<script> import { doSomeWork } from './utils'; $effect(() => { $inspect.trace(); // Logs which state changes caused this effect to run doSomeWork(); }); </script>