Skip to content

The $effect Rune

Effects synchronize with the outside world

Section titled “Effects synchronize with the outside world”

$effect runs a function after the DOM has updated, and re-runs it whenever the reactive values it reads change. It is for side effects — synchronizing Svelte state with things outside Svelte: the DOM directly, a third-party library, a subscription, a timer, logging, localStorage.

<script>
let count = $state(0);
$effect(() => {
// Runs after render, and again whenever count changes.
document.title = `Count: ${count}`;
});
</script>

Dependencies are tracked automatically, exactly like $derived — the effect reads count, so it re-runs when count changes. No dependency array.

If an effect sets something up that must be torn down — an interval, an event listener, a subscription — return a cleanup function. Svelte runs it before the effect re-runs and when the component is destroyed.

<script>
let seconds = $state(0);
$effect(() => {
const id = setInterval(() => seconds++, 1000);
return () => clearInterval(id); // cleanup: runs before re-run and on destroy
});
</script>

$effect runs after the DOM updates. Occasionally you need to read the DOM before it updates — for example, to capture scroll position before new content pushes it. $effect.pre runs before the DOM update instead, with the same tracking and cleanup rules.

<script>
let messages = $state([]);
$effect.pre(() => {
messages.length; // track the dependency
// read scroll position BEFORE the DOM updates with new messages
});
</script>

This is the most important guidance in the module. Effects are an escape hatch, and reaching for them to manage internal state is the most common Svelte 5 mistake. Before writing an effect, ask:

  • Computing a value from state? Use $derived, not an effect that assigns state.
  • Responding to a user action? Do the work in the event handler (onclick), not in an effect watching a variable.
  • Mirroring a prop into state? Usually a design smell — derive from the prop instead.
flowchart TB
  q["Need to react to state?"] --> derive["Computing a value? → $derived"]
  q --> handler["From a user action? → event handler"]
  q --> effect["Syncing with something OUTSIDE Svelte? → $effect"]
Choosing the right tool

An effect is the right tool only when you are bridging to something Svelte doesn’t control — the document title, a canvas, a websocket, localStorage, an analytics call. If the work stays inside Svelte’s reactive world, a rune like $derived almost always fits better.

What is `$effect` for?
How do you tear down a subscription or interval created in an effect?
You want to compute `fullName` from `first` and `last`. What should you use?
What is the difference between `$effect` and `$effect.pre`?