Side Effects with $effect
Running code in response to state changes — logging, syncing to localStorage, or updating the DOM directly.
2 min de lectura
Not everything that reacts to state change is a value you render. Sometimes you need to run actual code — saving to localStorage, logging analytics, manually adjusting a DOM element the framework doesn't manage for you. That's what $effect is for.
<script>
let count = $state(0);
$effect(() => {
console.log(`count is now ${count}`);
});
</script>
<button onclick={() => count++}>Increment</button>Like $derived, $effect automatically tracks whichever reactive values it reads — count, in this case — and re-runs whenever one of them changes. Unlike $derived, it doesn't produce a value; it just runs for its side effects, once after the component mounts and again after every relevant update.
Dependencies are inferred, not declared
If you've used React's useEffect, the biggest difference is the missing dependency array. React makes you list dependencies by hand (useEffect(fn, [count])) and it's easy to get that list wrong. Svelte's compiler statically analyzes what the effect function reads and wires up tracking automatically — there's no array to keep in sync with the function body.
<script>
let query = $state('');
let results = $state([]);
$effect(() => {
// reading `query` here means this effect re-runs whenever it changes
fetch(`/api/search?q=${query}`)
.then((r) => r.json())
.then((data) => (results = data));
});
</script>Cleaning up after yourself
If an effect sets something up — a subscription, a timer, an event listener — return a function from it, and Svelte will call that function right 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);
});
</script>
<p>{seconds}s elapsed</p>Without the cleanup function, every re-run of the effect would start a new interval without ever clearing the old one — a classic source of memory leaks and duplicated work.
$effect is a last resort, not a first instinct
It's tempting to reach for $effect for everything, since it feels like the most flexible tool. But if you're just computing a value, use $derived — it's simpler, and enforces that the calculation is a pure function of its inputs, with no room for stray side effects to sneak in. Save $effect for genuine side effects: things outside Svelte's own reactivity that need to be kept in sync with it, like the DOM APIs, network requests, or timers above.