CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-svelte

Svelte is a compiler-based UI framework that transforms declarative component code into efficient imperative JavaScript that surgically updates the DOM

Overview
Eval results
Files

reactivity.mddocs/reference/

Reactivity System

Svelte 5 introduces a powerful runes-based reactivity system that replaces the compiler-based reactivity of Svelte 4. The reactivity system consists of compile-time runes (special syntax processed by the compiler) and runtime APIs (reactive classes and functions).

Table of Contents

  • Runes (Compile-time Syntax)
  • Reactive Classes (svelte/reactivity)
  • Reactive Window Properties (svelte/reactivity/window)
  • Runtime Functions

Runes (Compile-time Syntax)

Runes are special compile-time syntax available in .svelte files and Svelte components. They are not runtime imports but are transformed by the compiler into reactive code.

State Runes

$state

Declares deeply reactive state. When any property of the state changes, components and effects that read that property are automatically re-run.

declare function $state<T>(initial: T): T;
declare function $state<T>(): T | undefined;

Usage:

<script>
	let count = $state(0);
	let user = $state({ name: 'Alice', age: 30 });
</script>

<button onclick={() => count++}>
	Clicked {count} times
</button>

<button onclick={() => user.age++}>
	{user.name} is {user.age} years old
</button>

The state is deeply reactive, meaning nested properties are also tracked:

let obj = $state({ nested: { value: 0 } });
obj.nested.value++; // triggers reactivity

$state.raw

Declares non-deeply-reactive state (shallow reactivity). Only the top-level value is reactive; nested properties are not tracked.

declare namespace $state {
	/**
	 * Declares non-deeply-reactive state (shallow reactivity).
	 * @param initial - Initial value
	 * @returns The state value with shallow reactivity
	 */
	export function raw<T>(initial: T): T;
	export function raw<T>(): T | undefined;
}

Usage:

<script>
	// Only changes to `items` itself are reactive, not changes to items' properties
	let items = $state.raw([
		{ id: 1, name: 'Item 1' },
		{ id: 2, name: 'Item 2' }
	]);
</script>

<button onclick={() => items = [...items, { id: 3, name: 'Item 3' }]}>
	Add item (reactive)
</button>

<button onclick={() => items[0].name = 'Updated'}>
	Update first item (not reactive)
</button>

$state.snapshot

Takes a static snapshot of proxied state. Returns a plain JavaScript object/array without any reactivity.

declare namespace $state {
	/**
	 * Takes a static snapshot of proxied state.
	 * @param state - The reactive state to snapshot
	 * @returns A non-reactive copy of the state
	 */
	export function snapshot<T>(state: T): Snapshot<T>;
}

Usage:

<script>
	let state = $state({ count: 0, items: [1, 2, 3] });

	function logSnapshot() {
		// Get a plain, non-reactive copy
		const snapshot = $state.snapshot(state);
		console.log(snapshot); // { count: 0, items: [1, 2, 3] }

		// snapshot is not reactive - changes won't trigger updates
		snapshot.count++; // no effect on UI
	}
</script>

Use this when you need to:

  • Pass state to external libraries that shouldn't trigger reactivity
  • Serialize state to JSON
  • Compare current and previous state values

$state.eager

Gets the latest value during async suspension. Useful when working with async data and you need the most current value.

declare namespace $state {
	/**
	 * Gets the latest value during async suspension.
	 * @param value - The value to eagerly evaluate
	 * @returns The eager value
	 * @since 5.42.0
	 */
	export function eager<T>(value: T): T;
}

Usage:

<script>
	let id = $state(1);

	// Without eager, `id` might be stale during async operations
	async function loadData() {
		const currentId = $state.eager(id); // always gets latest value
		const response = await fetch(`/api/data/${currentId}`);
		return await response.json();
	}
</script>

Derived Runes

$derived

Declares derived/computed state from expressions. The value is automatically recalculated when its dependencies change.

/**
 * Declares derived/computed state from expressions.
 * @param expression - The expression to derive the value from
 * @returns The derived value, automatically recalculated when dependencies change
 */
declare function $derived<T>(expression: T): T;

Usage:

<script>
	let count = $state(0);
	let doubled = $derived(count * 2);
	let message = $derived(`Count is ${count}`);
</script>

<p>{count} × 2 = {doubled}</p>
<p>{message}</p>

Derived values are read-only and only recalculate when accessed and their dependencies have changed.


$derived.by

Declares derived state with complex logic in a function. Use this when your derived value requires multiple statements or complex computation.

declare namespace $derived {
	/**
	 * Declares derived state with complex logic in a function.
	 * @param fn - Function that computes the derived value
	 * @returns The derived value
	 */
	export function by<T>(fn: () => T): T;
}

Usage:

<script>
	let numbers = $state([1, 2, 3, 4, 5]);

	let stats = $derived.by(() => {
		const sum = numbers.reduce((a, b) => a + b, 0);
		const avg = sum / numbers.length;
		const max = Math.max(...numbers);
		return { sum, avg, max };
	});
</script>

<p>Sum: {stats.sum}</p>
<p>Average: {stats.avg}</p>
<p>Max: {stats.max}</p>

Effect Runes

$effect

Runs side effects after DOM updates. Effects automatically track their dependencies and re-run when those dependencies change.

/**
 * Runs side effects after DOM updates.
 * @param fn - The effect function. Can return a cleanup function.
 */
declare function $effect(fn: () => void | (() => void)): void;

Usage:

<script>
	let count = $state(0);

	$effect(() => {
		// Runs after DOM updates whenever `count` changes
		console.log(`Count is now ${count}`);

		// Optional: return cleanup function
		return () => {
			console.log('Cleaning up previous effect');
		};
	});
</script>

<button onclick={() => count++}>Increment</button>

Best Practices:

  • Use for DOM manipulation, logging, analytics
  • Use for setting up subscriptions or event listeners
  • Always clean up subscriptions/listeners in the cleanup function
  • Don't use for deriving state (use $derived instead)

$effect.pre

Runs side effects before DOM updates. Useful when you need to measure DOM properties before they change.

declare namespace $effect {
	/**
	 * Runs side effects before DOM updates.
	 * @param fn - The effect function. Can return a cleanup function.
	 */
	export function pre(fn: () => void | (() => void)): void;
}

Usage:

<script>
	let items = $state([1, 2, 3]);
	let listElement;

	$effect.pre(() => {
		// Runs before the DOM updates
		if (listElement) {
			const oldHeight = listElement.offsetHeight;
			console.log('Height before update:', oldHeight);
		}
	});
</script>

<ul bind:this={listElement}>
	{#each items as item}
		<li>{item}</li>
	{/each}
</ul>

$effect.root

Creates a manually-controlled effect root that persists until explicitly destroyed. Returns a cleanup function.

declare namespace $effect {
	/**
	 * Creates manually-controlled effect root that persists until explicitly destroyed.
	 * @param fn - The effect function
	 * @returns A cleanup function to destroy the effect
	 */
	export function root(fn: () => void | (() => void)): () => void;
}

Usage:

<script>
	let count = $state(0);
	let cleanup;

	function startTracking() {
		cleanup = $effect.root(() => {
			$effect(() => {
				console.log('Count changed:', count);
			});
		});
	}

	function stopTracking() {
		cleanup?.(); // Destroy the effect
	}
</script>

<button onclick={startTracking}>Start tracking</button>
<button onclick={stopTracking}>Stop tracking</button>
<button onclick={() => count++}>Increment</button>

$effect.tracking

Returns true if code is running in a tracking context (inside an effect or derived).

declare namespace $effect {
	/**
	 * Returns true if code is running in a tracking context.
	 * @returns Whether the code is currently tracking reactive dependencies
	 */
	export function tracking(): boolean;
}

Usage:

<script>
	function doSomething() {
		if ($effect.tracking()) {
			console.log('Called from reactive context');
		} else {
			console.log('Called from non-reactive context');
		}
	}

	$effect(() => {
		doSomething(); // logs "Called from reactive context"
	});

	doSomething(); // logs "Called from non-reactive context"
</script>

$effect.pending

Returns the number of pending promises in current effects. Useful for showing loading states.

declare namespace $effect {
	/**
	 * Returns the number of pending promises in current effects.
	 * @returns The count of pending async operations
	 * @since 5.42.0
	 */
	export function pending(): number;
}

Usage:

<script>
	let id = $state(1);

	$effect(() => {
		const pending = $effect.pending();
		console.log(`${pending} operations pending`);
	});

	async function loadData() {
		await fetch(`/api/data/${id}`);
	}
</script>

Props Runes

$props

Declares component props with destructuring. Props are read-only by default.

/**
 * Declares component props with destructuring.
 * @returns The props object
 */
declare function $props(): any;

Usage:

<!-- MyComponent.svelte -->
<script>
	let {
		title,
		count = 0,  // with default value
		items = []  // with default array
	} = $props();
</script>

<h1>{title}</h1>
<p>Count: {count}</p>
<ul>
	{#each items as item}
		<li>{item}</li>
	{/each}
</ul>

Type-safe props with TypeScript:

<script lang="ts">
	interface Props {
		title: string;
		count?: number;
		items?: string[];
	}

	let { title, count = 0, items = [] }: Props = $props();
</script>

$props.id

Generates a unique ID for the component instance. Useful for accessibility attributes.

declare namespace $props {
	/**
	 * Generates a unique ID for the component instance.
	 * @returns A unique string identifier
	 * @since 5.20.0
	 */
	export function id(): string;
}

Usage:

<script>
	let { label } = $props();
	const inputId = $props.id();
</script>

<label for={inputId}>{label}</label>
<input id={inputId} type="text" />

Bindable Rune

$bindable

Declares a prop as bindable (two-way binding). Allows parent components to use bind: directive.

/**
 * Declares a prop as bindable (two-way binding).
 * @param fallback - Optional fallback value if prop is not provided
 * @returns The bindable prop value
 */
declare function $bindable<T>(fallback?: T): T;

Usage:

<!-- Counter.svelte -->
<script>
	let { count = $bindable(0) } = $props();
</script>

<button onclick={() => count++}>
	Count: {count}
</button>
<!-- Parent.svelte -->
<script>
	import Counter from './Counter.svelte';
	let value = $state(0);
</script>

<Counter bind:count={value} />
<p>Parent knows count is: {value}</p>

Inspect Runes

$inspect

Logs reactive values whenever they change. Only works in development mode.

/**
 * Logs reactive values whenever they change (dev mode only).
 * @param values - Values to inspect
 */
declare function $inspect<T extends any[]>(
	...values: T
): void;

Usage:

<script>
	let count = $state(0);
	let user = $state({ name: 'Alice' });

	// Logs to console whenever count or user changes
	$inspect(count, user);
</script>

<button onclick={() => count++}>Increment</button>
<input bind:value={user.name} />

$inspect.trace

Tracks and logs effect dependencies for debugging. Shows what state changes trigger effects.

declare namespace $inspect {
	/**
	 * Tracks and logs effect dependencies for debugging.
	 * @param name - Optional name for the trace
	 */
	export function trace(name?: string): void;
}

Usage:

<script>
	let count = $state(0);
	let doubled = $derived(count * 2);

	$effect(() => {
		$inspect.trace('count-effect');
		console.log(count, doubled);
	});
</script>

Host Rune

$host

Gets reference to custom element host when component is compiled as a custom element.

/**
 * Gets reference to custom element host when component is compiled as custom element.
 * @returns The host HTMLElement
 */
declare function $host<El extends HTMLElement = HTMLElement>(): El;

Usage:

<svelte:options customElement="my-counter" />

<script>
	let count = $state(0);
	const host = $host();

	$effect(() => {
		// Dispatch custom event on the host element
		host.dispatchEvent(new CustomEvent('countchange', {
			detail: { count }
		}));
	});
</script>

<button onclick={() => count++}>
	Count: {count}
</button>

Reactive Classes (svelte/reactivity)

The svelte/reactivity module provides reactive wrappers for built-in JavaScript objects. These classes maintain standard API compatibility while adding automatic reactivity.

SvelteMap<K, V>

A reactive version of the built-in Map object.

/**
 * A reactive version of the built-in Map object.
 * Reading contents of the map (by iterating, or by reading `map.size` or
 * calling `map.get(...)` or `map.has(...)`) in an effect or derived will
 * cause it to be re-evaluated as necessary when the map is updated.
 *
 * Note that values in a reactive map are not made deeply reactive.
 */
export class SvelteMap<K, V> extends Map<K, V> {
	constructor(value?: Iterable<readonly [K, V]> | null | undefined);
	set(key: K, value: V): this;
}

Usage:

<script>
	import { SvelteMap } from 'svelte/reactivity';

	let board = new SvelteMap();
	let player = $state('x');

	function makeMove(position) {
		board.set(position, player);
		player = player === 'x' ? 'o' : 'x';
	}
</script>

<div class="board">
	{#each Array(9) as _, i}
		<button
			disabled={board.has(i)}
			onclick={() => makeMove(i)}
		>
			{board.get(i) ?? ''}
		</button>
	{/each}
</div>

<p>Size: {board.size}</p>

All standard Map methods are available:

  • set(key, value) - Add or update entry (triggers reactivity)
  • get(key) - Get value (tracked)
  • has(key) - Check existence (tracked)
  • delete(key) - Remove entry (triggers reactivity)
  • clear() - Remove all entries (triggers reactivity)
  • size - Number of entries (tracked)
  • keys(), values(), entries() - Iterators (tracked)
  • forEach() - Iterate over entries (tracked)

SvelteSet<T>

A reactive version of the built-in Set object.

/**
 * A reactive version of the built-in Set object.
 * Reading contents of the set (by iterating, or by reading `set.size` or
 * calling `set.has(...)`) in an effect or derived will cause it to be
 * re-evaluated as necessary when the set is updated.
 *
 * Note that values in a reactive set are not made deeply reactive.
 */
export class SvelteSet<T> extends Set<T> {
	constructor(value?: Iterable<T> | null | undefined);
	add(value: T): this;
}

Usage:

<script>
	import { SvelteSet } from 'svelte/reactivity';

	let selected = new SvelteSet();

	function toggle(item) {
		if (selected.has(item)) {
			selected.delete(item);
		} else {
			selected.add(item);
		}
	}
</script>

{#each ['🙈', '🙉', '🙊'] as emoji}
	<button onclick={() => toggle(emoji)}>
		{emoji} {selected.has(emoji) ? '✓' : ''}
	</button>
{/each}

<p>Selected: {selected.size}</p>
<button onclick={() => selected.clear()}>Clear</button>

All standard Set methods are available:

  • add(value) - Add item (triggers reactivity)
  • has(value) - Check existence (tracked)
  • delete(value) - Remove item (triggers reactivity)
  • clear() - Remove all items (triggers reactivity)
  • size - Number of items (tracked)
  • keys(), values(), entries() - Iterators (tracked)
  • forEach() - Iterate over items (tracked)

SvelteDate

A reactive version of the built-in Date object.

/**
 * A reactive version of the built-in Date object.
 * Calling any method that modifies the date will cause effects and derived
 * values that read from it to re-run.
 */
export class SvelteDate extends Date {
	constructor(...params: any[]);
}

Usage:

<script>
	import { SvelteDate } from 'svelte/reactivity';

	const date = new SvelteDate();

	$effect(() => {
		const interval = setInterval(() => {
			date.setTime(Date.now());
		}, 1000);

		return () => clearInterval(interval);
	});

	const formatter = new Intl.DateTimeFormat('en', {
		hour12: true,
		hour: 'numeric',
		minute: '2-digit',
		second: '2-digit'
	});
</script>

<p>The time is {formatter.format(date)}</p>

All standard Date methods are available. Methods that mutate the date trigger reactivity:

  • setTime(), setFullYear(), setMonth(), setDate()
  • setHours(), setMinutes(), setSeconds(), setMilliseconds()
  • And all UTC variants

SvelteURL

A reactive version of the built-in URL object.

/**
 * A reactive version of the built-in URL object.
 * Reading properties of the URL (such as `url.href` or `url.pathname`) in
 * an effect or derived will cause it to be re-evaluated as necessary when
 * the URL changes.
 *
 * The `searchParams` property is an instance of SvelteURLSearchParams.
 */
export class SvelteURL extends URL {
	get searchParams(): SvelteURLSearchParams;
}

Usage:

<script>
	import { SvelteURL } from 'svelte/reactivity';

	const url = new SvelteURL('https://example.com/path');
</script>

<!-- Changes to these... -->
<input bind:value={url.protocol} />
<input bind:value={url.hostname} />
<input bind:value={url.pathname} />

<hr />

<!-- will update `href` and vice versa -->
<input bind:value={url.href} size="65" />

Reactive URL properties:

  • href, origin, protocol, username, password
  • host, hostname, port
  • pathname, search, hash
  • searchParams (returns SvelteURLSearchParams)

SvelteURLSearchParams

A reactive version of the built-in URLSearchParams object.

/**
 * A reactive version of the built-in URLSearchParams object.
 * Reading its contents (by iterating, or by calling `params.get(...)` or
 * `params.getAll(...)`) in an effect or derived will cause it to be
 * re-evaluated as necessary when the params are updated.
 */
export class SvelteURLSearchParams extends URLSearchParams {
	constructor(init?: string | URLSearchParams | Record<string, string> | Iterable<[string, string]> | Array<[string, string]>);
}

Usage:

<script>
	import { SvelteURLSearchParams } from 'svelte/reactivity';

	const params = new SvelteURLSearchParams('message=hello&name=world');

	let key = $state('');
	let value = $state('');
</script>

<input bind:value={key} placeholder="Key" />
<input bind:value={value} placeholder="Value" />
<button onclick={() => params.append(key, value)}>Append</button>

<p>?{params.toString()}</p>

{#each params as [key, value]}
	<p>{key}: {value}</p>
{/each}

All standard URLSearchParams methods are available:

  • append(key, value) - Add parameter (triggers reactivity)
  • set(key, value) - Set parameter (triggers reactivity)
  • delete(key) - Remove parameter (triggers reactivity)
  • get(key) - Get value (tracked)
  • getAll(key) - Get all values (tracked)
  • has(key) - Check existence (tracked)
  • toString() - Serialize to string (tracked)

MediaQuery

Creates a media query and provides a current property that reflects whether or not it matches.

/**
 * Creates a media query and provides a `current` property that reflects
 * whether or not it matches.
 *
 * Use it carefully — during server-side rendering, there is no way to know
 * what the correct value should be, potentially causing content to change
 * upon hydration. If you can use the media query in CSS to achieve the same
 * effect, do that.
 *
 * @since 5.7.0
 */
export class MediaQuery {
	/**
	 * @param query - A media query string
	 * @param fallback - Fallback value for the server
	 */
	constructor(query: string, fallback?: boolean | undefined);

	/**
	 * Whether the media query currently matches
	 */
	get current(): boolean;
}

Usage:

<script>
	import { MediaQuery } from 'svelte/reactivity';

	const large = new MediaQuery('(min-width: 800px)');
	const dark = new MediaQuery('(prefers-color-scheme: dark)');
	const reduced = new MediaQuery('(prefers-reduced-motion: reduce)');
</script>

<h1>{large.current ? 'Large screen' : 'Small screen'}</h1>
<p>Theme: {dark.current ? 'Dark' : 'Light'}</p>
<p>Motion: {reduced.current ? 'Reduced' : 'Normal'}</p>

SSR Considerations:

// Provide fallback for server-side rendering
const large = new MediaQuery('(min-width: 800px)', false);

createSubscriber()

Creates a subscriber function that integrates external event-based systems with Svelte's reactivity.

/**
 * Returns a `subscribe` function that integrates external event-based systems
 * with Svelte's reactivity. It's particularly useful for integrating with web
 * APIs like `MediaQuery`, `IntersectionObserver`, or `WebSocket`.
 *
 * If `subscribe` is called inside an effect (including indirectly, for example
 * inside a getter), the `start` callback will be called with an `update`
 * function. Whenever `update` is called, the effect re-runs.
 *
 * If `start` returns a cleanup function, it will be called when the effect
 * is destroyed.
 *
 * If `subscribe` is called in multiple effects, `start` will only be called
 * once as long as the effects are active, and the returned teardown function
 * will only be called when all effects are destroyed.
 *
 * @param start - Function called when first subscriber subscribes
 * @returns A subscribe function to use in reactive contexts
 * @since 5.7.0
 */
export function createSubscriber(
	start: (update: () => void) => (() => void) | void
): () => void;

Usage - Custom MediaQuery implementation:

import { createSubscriber } from 'svelte/reactivity';
import { on } from 'svelte/events';

export class CustomMediaQuery {
	#query;
	#subscribe;

	constructor(query) {
		this.#query = window.matchMedia(`(${query})`);

		this.#subscribe = createSubscriber((update) => {
			// When the 'change' event occurs, re-run any effects that read this.current
			const off = on(this.#query, 'change', update);

			// Stop listening when all the effects are destroyed
			return () => off();
		});
	}

	get current() {
		// This makes the getter reactive, if read in an effect
		this.#subscribe();

		// Return the current state of the query, whether or not we're in an effect
		return this.#query.matches;
	}
}

Usage - IntersectionObserver:

import { createSubscriber } from 'svelte/reactivity';

export class IntersectionState {
	#element;
	#subscribe;
	#isIntersecting = false;

	constructor(element, options) {
		this.#element = element;

		this.#subscribe = createSubscriber((update) => {
			const observer = new IntersectionObserver(([entry]) => {
				this.#isIntersecting = entry.isIntersecting;
				update();
			}, options);

			observer.observe(this.#element);

			return () => observer.disconnect();
		});
	}

	get isIntersecting() {
		this.#subscribe();
		return this.#isIntersecting;
	}
}

Usage - WebSocket:

import { createSubscriber } from 'svelte/reactivity';

export class ReactiveWebSocket {
	#ws;
	#subscribe;
	#data = null;

	constructor(url) {
		this.#ws = new WebSocket(url);

		this.#subscribe = createSubscriber((update) => {
			const handleMessage = (event) => {
				this.#data = event.data;
				update();
			};

			this.#ws.addEventListener('message', handleMessage);

			return () => {
				this.#ws.removeEventListener('message', handleMessage);
				this.#ws.close();
			};
		});
	}

	get data() {
		this.#subscribe();
		return this.#data;
	}

	send(message) {
		this.#ws.send(message);
	}
}

Reactive Window Properties (svelte/reactivity/window)

The svelte/reactivity/window module provides reactive properties that automatically update based on window state changes.

All exports have a .current getter that returns the current value. Reading .current in an effect or derived makes it reactive.

Window Dimensions

/**
 * `scrollX.current` is a reactive view of `window.scrollX`.
 * On the server it is `undefined`.
 * @since 5.11.0
 */
export const scrollX: ReactiveValue<number | undefined>;

/**
 * `scrollY.current` is a reactive view of `window.scrollY`.
 * On the server it is `undefined`.
 * @since 5.11.0
 */
export const scrollY: ReactiveValue<number | undefined>;

/**
 * `innerWidth.current` is a reactive view of `window.innerWidth`.
 * On the server it is `undefined`.
 * @since 5.11.0
 */
export const innerWidth: ReactiveValue<number | undefined>;

/**
 * `innerHeight.current` is a reactive view of `window.innerHeight`.
 * On the server it is `undefined`.
 * @since 5.11.0
 */
export const innerHeight: ReactiveValue<number | undefined>;

/**
 * `outerWidth.current` is a reactive view of `window.outerWidth`.
 * On the server it is `undefined`.
 * @since 5.11.0
 */
export const outerWidth: ReactiveValue<number | undefined>;

/**
 * `outerHeight.current` is a reactive view of `window.outerHeight`.
 * On the server it is `undefined`.
 * @since 5.11.0
 */
export const outerHeight: ReactiveValue<number | undefined>;

Usage:

<script>
	import {
		scrollX,
		scrollY,
		innerWidth,
		innerHeight
	} from 'svelte/reactivity/window';

	// Automatically reactive - updates when window size/scroll changes
	let isScrolled = $derived(scrollY.current > 100);
	let isMobile = $derived(innerWidth.current < 768);
</script>

<div class:scrolled={isScrolled}>
	<p>Window: {innerWidth.current} × {innerHeight.current}</p>
	<p>Scroll: {scrollX.current}, {scrollY.current}</p>
	<p>Device: {isMobile ? 'Mobile' : 'Desktop'}</p>
</div>

Window Position

/**
 * `screenLeft.current` is a reactive view of `window.screenLeft`.
 * It is updated inside a `requestAnimationFrame` callback.
 * On the server it is `undefined`.
 * @since 5.11.0
 */
export const screenLeft: ReactiveValue<number | undefined>;

/**
 * `screenTop.current` is a reactive view of `window.screenTop`.
 * It is updated inside a `requestAnimationFrame` callback.
 * On the server it is `undefined`.
 * @since 5.11.0
 */
export const screenTop: ReactiveValue<number | undefined>;

Usage:

<script>
	import { screenLeft, screenTop } from 'svelte/reactivity/window';
</script>

<p>Window position: {screenLeft.current}, {screenTop.current}</p>

Network Status

/**
 * `online.current` is a reactive view of `navigator.onLine`.
 * On the server it is `undefined`.
 * @since 5.11.0
 */
export const online: ReactiveValue<boolean | undefined>;

Usage:

<script>
	import { online } from 'svelte/reactivity/window';
</script>

{#if online.current === false}
	<div class="offline-banner">
		You are currently offline
	</div>
{/if}

Device Pixel Ratio

/**
 * `devicePixelRatio.current` is a reactive view of `window.devicePixelRatio`.
 * On the server it is `undefined`.
 *
 * Note that behaviour differs between browsers — on Chrome it will respond
 * to the current zoom level, on Firefox and Safari it won't.
 *
 * @since 5.11.0
 */
export const devicePixelRatio: {
	get current(): number | undefined;
};

Usage:

<script>
	import { devicePixelRatio } from 'svelte/reactivity/window';

	let isHighDPI = $derived(devicePixelRatio.current >= 2);
</script>

<img
	src={isHighDPI ? 'image@2x.png' : 'image.png'}
	alt="Responsive to pixel density"
/>

Runtime Functions

Runtime functions from the main svelte module that control reactivity behavior.

untrack()

Runs a function without creating reactive dependencies. Useful when you want to read state without triggering re-runs.

/**
 * Runs function without creating reactive dependencies.
 * @param fn - Function to run without tracking
 * @returns The return value of the function
 */
export function untrack<T>(fn: () => T): T;

Usage:

<script>
	import { untrack } from 'svelte';

	let count = $state(0);
	let name = $state('Alice');

	$effect(() => {
		// This effect only re-runs when `count` changes
		console.log('Count:', count);

		// Reading `name` doesn't create a dependency
		const currentName = untrack(() => name);
		console.log('Name (untracked):', currentName);
	});
</script>

<button onclick={() => count++}>Increment</button>
<input bind:value={name} />

Common use cases:

  • Reading state for logging without creating dependencies
  • Accessing stable values that shouldn't trigger re-runs
  • Breaking reactive cycles

fork()

Creates a speculative state fork in which state changes are evaluated but not applied to the DOM. Useful for preloading data.

/**
 * Creates a 'fork', in which state changes are evaluated but not applied
 * to the DOM. This is useful for speculatively loading data (for example)
 * when you suspect that the user is about to take some action.
 *
 * The `fn` parameter is a synchronous function that modifies some state.
 * The state changes will be reverted after the fork is initialised, then
 * reapplied if and when the fork is eventually committed.
 *
 * When it becomes clear that a fork will not be committed (e.g. because
 * the user navigated elsewhere), it must be discarded to avoid leaking memory.
 *
 * @param fn - Synchronous function that modifies state
 * @returns Fork object with commit() and discard() methods
 * @since 5.42.0
 */
export function fork(fn: () => void): Fork;

export interface Fork {
	/**
	 * Commit the fork. The promise will resolve once the state change
	 * has been applied to the DOM.
	 */
	commit(): Promise<void>;

	/**
	 * Discard the fork
	 */
	discard(): void;
}

Usage - Preloading data on hover:

<script>
	import { fork } from 'svelte';

	let data = $state(null);
	let currentFork = null;

	async function loadData(id) {
		const response = await fetch(`/api/data/${id}`);
		data = await response.json();
	}

	function preload(id) {
		// Create a fork to speculatively load data
		currentFork = fork(() => {
			loadData(id);
		});
	}

	function navigate(id) {
		if (currentFork) {
			// Commit the speculative changes
			currentFork.commit();
			currentFork = null;
		} else {
			loadData(id);
		}
	}

	function cancelPreload() {
		if (currentFork) {
			currentFork.discard();
			currentFork = null;
		}
	}
</script>

<a
	href="/item/123"
	onmouseenter={() => preload(123)}
	onmouseleave={cancelPreload}
	onclick={(e) => {
		e.preventDefault();
		navigate(123);
	}}
>
	View Item
</a>

Key points:

  • The function passed to fork() must be synchronous
  • State changes are temporarily reverted after initialization
  • Call commit() to apply the changes permanently
  • Call discard() to abandon the fork and free memory
  • Useful for frameworks like SvelteKit to preload data on link hover

getAbortSignal()

Returns an AbortSignal that aborts when the current derived or effect re-runs or is destroyed.

/**
 * Returns an AbortSignal that aborts when the current derived or effect
 * re-runs or is destroyed.
 *
 * Must be called while a derived or effect is running.
 */
export function getAbortSignal(): AbortSignal;

Usage - Canceling fetch requests:

<script>
	import { getAbortSignal } from 'svelte';

	let id = $state(1);

	async function getData(id) {
		const response = await fetch(`/api/items/${id}`, {
			signal: getAbortSignal()
		});

		return await response.json();
	}

	// Automatically cancels previous request when id changes
	const data = $derived(await getData(id));
</script>

<input type="number" bind:value={id} />

{#if data}
	<pre>{JSON.stringify(data, null, 2)}</pre>
{/if}

Usage - Aborting long-running operations:

<script>
	import { getAbortSignal } from 'svelte';

	let query = $state('');

	$effect(() => {
		const signal = getAbortSignal();
		const timer = setTimeout(() => {
			if (!signal.aborted) {
				performSearch(query);
			}
		}, 300);

		signal.addEventListener('abort', () => {
			clearTimeout(timer);
		});
	});
</script>

Key points:

  • Can only be called inside $effect or $derived
  • Automatically aborts when dependencies change or effect is destroyed
  • Works with any API that accepts AbortSignal (fetch, DOM APIs, etc.)
  • Prevents memory leaks and unnecessary work

Best Practices

State Management

  1. Use $state for component-local state:

    <script>
        let count = $state(0);
    </script>
  2. Use $derived for computed values:

    <script>
        let count = $state(0);
        let doubled = $derived(count * 2);
    </script>
  3. Use $state.raw for large data structures:

    <script>
        // Only top-level changes are tracked
        let bigList = $state.raw([/* thousands of items */]);
    </script>

Effects

  1. Prefer $derived over $effect for deriving state:

    <!-- Good -->
    <script>
        let count = $state(0);
        let doubled = $derived(count * 2);
    </script>
    
    <!-- Bad -->
    <script>
        let count = $state(0);
        let doubled = $state(0);
        $effect(() => {
            doubled = count * 2; // Don't do this
        });
    </script>
  2. Always clean up in effects:

    <script>
        $effect(() => {
            const interval = setInterval(() => {
                // ...
            }, 1000);
    
            return () => clearInterval(interval);
        });
    </script>
  3. Use untrack() to avoid unwanted dependencies:

    <script>
        import { untrack } from 'svelte';
    
        let x = $state(0);
        let y = $state(0);
    
        $effect(() => {
            console.log('x changed:', x);
            // Reading y doesn't create dependency
            const currentY = untrack(() => y);
        });
    </script>

Reactive Classes

  1. Use reactive classes for data structures:

    <script>
        import { SvelteMap, SvelteSet } from 'svelte/reactivity';
    
        let users = new SvelteMap();
        let selectedIds = new SvelteSet();
    </script>
  2. Remember they use shallow reactivity:

    <script>
        import { SvelteMap } from 'svelte/reactivity';
    
        let map = new SvelteMap();
        map.set('user', { name: 'Alice' });
    
        // This triggers reactivity
        map.set('user', { name: 'Bob' });
    
        // This does NOT trigger reactivity
        map.get('user').name = 'Charlie';
    </script>

Performance

  1. Use $state.raw for performance-critical data:

    let hugeArray = $state.raw(new Array(10000));
  2. Debounce expensive derived computations:

    <script>
        let input = $state('');
        let debounced = $state('');
    
        $effect(() => {
            const timer = setTimeout(() => {
                debounced = input;
            }, 300);
    
            return () => clearTimeout(timer);
        });
    
        let expensiveResult = $derived(computeExpensive(debounced));
    </script>
  3. Use getAbortSignal() to cancel stale requests:

    const data = $derived(await fetch('/api', {
        signal: getAbortSignal()
    }));

Migration from Svelte 4

State

<!-- Svelte 4 -->
<script>
    let count = 0;
</script>

<!-- Svelte 5 -->
<script>
    let count = $state(0);
</script>

Derived/Computed

<!-- Svelte 4 -->
<script>
    let count = 0;
    $: doubled = count * 2;
</script>

<!-- Svelte 5 -->
<script>
    let count = $state(0);
    let doubled = $derived(count * 2);
</script>

Effects

<!-- Svelte 4 -->
<script>
    let count = 0;
    $: {
        console.log(count);
    }
</script>

<!-- Svelte 5 -->
<script>
    let count = $state(0);
    $effect(() => {
        console.log(count);
    });
</script>

Props

<!-- Svelte 4 -->
<script>
    export let title;
    export let count = 0;
</script>

<!-- Svelte 5 -->
<script>
    let { title, count = 0 } = $props();
</script>

Two-way Binding

<!-- Svelte 4 -->
<!-- Parent.svelte -->
<Child bind:value />

<!-- Child.svelte -->
<script>
    export let value;
</script>

<!-- Svelte 5 -->
<!-- Parent.svelte -->
<Child bind:value />

<!-- Child.svelte -->
<script>
    let { value = $bindable() } = $props();
</script>

See Also

  • Lifecycle Functions - Component lifecycle management
  • Store System - Alternative reactive state management

Install with Tessl CLI

npx tessl i tessl/npm-svelte

docs

index.md

tile.json