$effect rune overview
Effects are functions that run when state updates and can be used for tasks like calling third-party libraries, drawing on canvas elements, or making network requests. Effects only run in the browser, not during server-side rendering.
When not to update state inside $effect
You should generally not update state inside effects, as it will make code more convoluted and often lead to never-ending update cycles.
$effect dependency tracking mechanism
$effect automatically picks up any reactive values ($state, $derived, $props) that are synchronously read inside its function body (including indirectly, via function calls) and registers them as dependencies. When those dependencies change, the $effect schedules a re-run.
Asynchronous values not tracked in $effect
Values that are read asynchronously — after an await or inside a setTimeout, for example — will not be tracked as dependencies and will not trigger effect re-runs.
$effect only depends on object reassignment, not property mutations
An effect only reruns when the object it reads changes, not when a property inside it changes. If $state or $derived are used directly inside the $effect (for example, during creation of a reactive class), those values will not be treated as dependencies.
$effect lifecycle and batching
Effects run after the component has been mounted to the DOM, and in a microtask after state changes. Re-runs are batched (changing multiple values in the same moment won't cause multiple separate runs), and happen after any DOM updates have been applied.
Where $effect can be used
You can use $effect anywhere, not just at the top level of a component, as long as it is called while a parent effect is running.
$effect teardown functions
An effect can return a teardown function which will run immediately before the effect re-runs. Teardown functions also run when the effect is destroyed (when the component is unmounted) or when the parent effect re-runs.
$effect.pre rune for running before DOM updates
$effect.pre runs code before DOM updates that are scheduled after it, not before every DOM mutation. DOM of parent components may already be updated. When using await expressions, block updates like {#if ...} and {#each ...} in the same component also run before $effect.pre. Apart from the timing, $effect.pre works exactly like $effect.
$effect.tracking rune
$effect.tracking() is an advanced feature that tells you whether code is running inside a tracking context, such as an effect or inside your template. It returns true when inside an effect or template, false otherwise. It is used to implement abstractions like createSubscriber.
$effect.pending rune
When using await in components, $effect.pending() returns how many promises are pending in the current boundary, not including child boundaries.
$effect.root rune for non-tracked scope
$effect.root is an advanced feature that creates a non-tracked scope that doesn't auto-cleanup. This is useful for nested effects that you want to manually control. This rune also allows for the creation of effects outside of the component initialisation phase. It returns a destroy function that can be called to clean up the effect.
Use $derived instead of $effect for state synchronization
Instead of using $effect to synchronize state (like setting doubled = count * 2), use $derived. For more complicated expressions, you can use $derived.by.
Avoid circular state updates with effects
Do not use effects to create circular dependencies between state values (for example, one effect setting state and another reading it). Instead, use derived values or function bindings. If you absolutely have to update $state within an effect and run into an infinite loop because you read and write to the same $state, use untrack.
Effect with conditional dependencies
An effect only depends on the values that it read the last time it ran. If a conditional branch changes, the set of tracked dependencies changes. For example, if condition is true and color is evaluated, changes to either condition or color will cause the effect to re-run. If condition becomes false and color is not evaluated, the effect will only re-run when condition changes.
$effect example with canvas drawing
Example: Effects can be used to draw on canvas elements. When $effect is used with color and size state variables, any synchronous read of these variables inside the effect function will cause the effect to re-run whenever those variables change. The canvas drawing code will update automatically: const context = canvas.getContext('2d'); context.clearRect(0, 0, canvas.width, canvas.height); context.fillStyle = color; context.fillRect(0, 0, size, size);
$effect.pre example with autoscroll
Example of $effect.pre for autoscrolling a message container before DOM updates: $effect.pre(() => { if (!div) return; messages.length; if (div.offsetHeight + div.scrollTop > div.scrollHeight - 20) { tick().then(() => { div.scrollTo(0, div.scrollHeight); }); } });
$effect.tracking example
Example: $effect.tracking() returns false in component setup, true when inside an effect, and true when evaluated in the template. This allows conditional creation of listeners only when values are being tracked.
$effect.root example
Example: const destroy = $effect.root(() => { $effect(() => { // setup }); return () => { // cleanup }; }); // later... destroy();
$effect as escape hatch pattern
$effect is best considered an escape hatch — useful for things like analytics and direct DOM manipulation — rather than a tool you should use frequently. In particular, avoid using it to synchronise state.