Svelte 5 introduces runes - a powerful reactivity system that replaces the previous assignment-based reactivity. Runes provide explicit, fine-grained reactivity with better TypeScript support and more predictable behavior.
Declares reactive state that triggers updates when changed.
/**
* Declares reactive state
* @param initial - Initial value for the state
* @returns Reactive state value
*/
declare function $state<T>(initial: T): T;
declare function $state<T>(): T | undefined;Usage Examples:
// Basic state
let count = $state(0);
let name = $state("Alice");
let items = $state([]);
// State without initial value
let user = $state(); // undefined initially
// Object state (deeply reactive by default)
let settings = $state({
theme: "dark",
language: "en",
notifications: {
email: true,
push: false
}
});
// Updating state triggers reactivity
count += 1; // This will trigger updates
settings.theme = "light"; // This will trigger updates
settings.notifications.email = false; // Deep updates work tooDeclares state that is not deeply reactive - you must reassign the entire value to trigger updates.
/**
* Declares state that is not made deeply reactive
* @param initial - Initial value for the raw state
* @returns Non-deeply reactive state value
*/
declare function raw<T>(initial: T): T;
declare function raw<T>(): T | undefined;Usage Examples:
let items = $state.raw([{ id: 1, name: "Item 1" }]);
// This won't trigger updates
items[0].name = "Updated Item";
// This will trigger updates
items = [...items, { id: 2, name: "Item 2" }];
items = items.map(item =>
item.id === 1 ? { ...item, name: "Updated Item" } : item
);Takes a static snapshot of deeply reactive state, useful for logging or serialization.
/**
* Takes a static snapshot of a deeply reactive state proxy
* @param state - The state to snapshot
* @returns Static snapshot of the state
*/
declare function snapshot<T>(state: T): Snapshot<T>;Usage Examples:
let counter = $state({ count: 0, history: [] });
function logState() {
// Will log the actual object, not the Proxy
console.log($state.snapshot(counter));
}
function saveToLocalStorage() {
const snapshot = $state.snapshot(counter);
localStorage.setItem("counter", JSON.stringify(snapshot));
}Creates derived state that automatically updates when its dependencies change.
/**
* Declares derived state that depends on other reactive values
* @param expression - Expression that computes the derived value
* @returns Derived reactive value
*/
declare function $derived<T>(expression: T): T;Usage Examples:
let count = $state(0);
let name = $state("Alice");
// Simple derived values
let doubled = $derived(count * 2);
let greeting = $derived(`Hello, ${name}!`);
let isEven = $derived(count % 2 === 0);
// Derived from multiple sources
let summary = $derived(`${name} has clicked ${count} times`);
// Complex derived values
let expensiveComputation = $derived(() => {
return someHeavyCalculation(count);
});Creates complex derived state using a function for more elaborate computations.
/**
* Creates complex derived state using a function
* @param fn - Function that computes the derived value
* @returns Derived reactive value
*/
declare function by<T>(fn: () => T): T;Usage Examples:
let numbers = $state([1, 2, 3, 4, 5]);
let total = $derived.by(() => {
let result = 0;
for (const n of numbers) {
result += n;
}
return result;
});
let filtered = $derived.by(() => {
return numbers.filter(n => n > 2);
});
// Complex async-like patterns (though derived should be sync)
let processedData = $derived.by(() => {
if (!rawData.length) return [];
return rawData
.map(item => processItem(item))
.filter(item => item.isValid)
.sort((a, b) => a.priority - b.priority);
});Runs side effects when reactive dependencies change.
/**
* Runs code when dependencies change, after DOM updates
* @param fn - Function to run, can return cleanup function
*/
declare function $effect(fn: () => void | (() => void)): void;Usage Examples:
let count = $state(0);
let name = $state("Alice");
// Basic effect
$effect(() => {
console.log(`Count is now ${count}`);
});
// Effect with cleanup
$effect(() => {
const interval = setInterval(() => {
console.log(`Count: ${count}`);
}, 1000);
return () => clearInterval(interval);
});
// Effect with multiple dependencies
$effect(() => {
document.title = `${name} - Count: ${count}`;
});
// Conditional effects
$effect(() => {
if (count > 10) {
console.log("Count is getting high!");
}
});Runs effects before DOM updates, useful for reading DOM state before changes.
/**
* Runs code before DOM updates when dependencies change
* @param fn - Function to run, can return cleanup function
*/
declare function pre(fn: () => void | (() => void)): void;Usage Examples:
let items = $state([]);
let container;
$effect.pre(() => {
if (container) {
const scrollableDistance = container.scrollHeight - container.offsetHeight;
const shouldAutoScroll = container.scrollTop > (scrollableDistance - 20);
if (shouldAutoScroll) {
// Will scroll after DOM updates
$effect(() => {
container.scrollTo(0, container.scrollHeight);
});
}
}
});Creates a non-tracked effect scope that doesn't auto-cleanup and can be manually controlled.
/**
* Creates a non-tracked scope that doesn't auto-cleanup
* @param fn - Function to run, can return cleanup function
* @returns Cleanup function to manually destroy the effect
*/
declare function root(fn: () => void | (() => void)): () => void;Usage Examples:
let count = $state(0);
const cleanup = $effect.root(() => {
$effect(() => {
console.log(`Count: ${count}`);
});
return () => {
console.log("Root effect cleaned up");
};
});
// Later, manually cleanup
cleanup();Checks whether code is running inside a tracking context.
/**
* Checks if code is running inside a tracking context
* @returns true if inside an effect or template, false otherwise
*/
declare function tracking(): boolean;Usage Examples:
function myUtility() {
if ($effect.tracking()) {
// We're inside an effect or template, safe to read reactive state
return someReactiveValue;
} else {
// Not in tracking context, perhaps return cached value
return cachedValue;
}
}Declares component props with proper typing and reactivity.
/**
* Declares the props that a component accepts
* @returns Props object with reactive properties
*/
declare function $props(): any;Usage Examples:
// Basic props
let { name, age, email } = $props();
// Props with defaults
let { theme = "light", size = "medium" } = $props();
// Typed props (in TypeScript)
interface Props {
name: string;
age?: number;
onSubmit?: (data: any) => void;
}
let { name, age = 0, onSubmit }: Props = $props();
// Bindable props
let { value = $bindable() } = $props();Declares a prop as bindable, allowing parent components to bind to it.
/**
* Declares a prop as bindable
* @param fallback - Default value if not bound
* @returns Bindable prop value
*/
declare function $bindable<T>(fallback?: T): T;Usage Examples:
// Child component
let { value = $bindable("") } = $props();
// Parent can now use bind:value
// <ChildComponent bind:value={parentValue} />
// With default value
let { isOpen = $bindable(false) } = $props();
// The parent can bind to it
// <Modal bind:isOpen={showModal} />Development utility for inspecting reactive values and their changes.
/**
* Inspects reactive values and logs changes
* @param values - Values to inspect
* @returns Object with 'with' method for custom inspection
*/
declare function $inspect<T extends any[]>(
...values: T
): { with: (fn: (type: 'init' | 'update', ...values: T) => void) => void };Usage Examples:
let count = $state(0);
let name = $state("Alice");
// Basic inspection (logs to console)
$inspect(count);
$inspect(count, name);
// Custom inspection
$inspect(count).with((type, value) => {
console.log(`${type}: count is ${value}`);
});
$inspect(count, name).with((type, countVal, nameVal) => {
if (type === 'update') {
console.log(`Update: ${nameVal} has count ${countVal}`);
}
});type Snapshot<T> = T extends Primitive
? T
: T extends Cloneable
? NonReactive<T>
: T extends { toJSON(): infer R }
? R
: T extends Array<infer U>
? Array<Snapshot<U>>
: T extends object
? { [K in keyof T]: Snapshot<T[K]> }
: never;
type Primitive = string | number | boolean | null | undefined;
type NotFunction<T> = T extends Function ? never : T;let count = 0 with let count = $state(0)$: doubled = count * 2 with let doubled = $derived(count * 2)$: { console.log(count); } with $effect(() => { console.log(count); })$props() instead of export let for component props