Understanding Svelte 5 Runes: $derived vs $effect

Published:
April 16, 2025
April 16, 2025
Updated:
April 16, 2025

Svelte 5 introduces a powerful new reactive primitive system called "runes" that simplifies state management. Let's explore what runes are and dive deep into two important ones: $derived and $effect

To demonstrate how to use these runes we will use a water tracker application that I've built. You can find the source code here: https://github.com/mikhail-karan/water-tracker-svelte5

What are Svelte 5 Runes?

Runes are special primitives in Svelte 5 marked with a $ prefix that enable fine-grained reactivity. Unlike Svelte 4's compiler-based reactivity, runes bring reactivity directly into the JavaScript runtime, making it more explicit and portable.

The main runes include:

  • $state - Declares reactive state
  • $derived - Computes values from state
  • $effect - Runs side effects when dependencies change
  • $props - Handles component props

The Power of $derived

$derived is used to compute values that depend on reactive state. It automatically updates whenever its dependencies change, and importantly, it's memoized (calculated only when needed).

From our water tracker app:

1const blocksToFill = $derived(Math.min(Math.floor(waterConsumed / 100), 20));
2const waterBlocks = $derived(Array(20)        
3	.fill(false)        
4	.map((_, index) => index < blocksToFill)); // 20 blocks representing 100ml each
5const lastDrinkDerived = $derived(waterConsumed > 0 ? new Date() : null);

Here we use $derived to:

  1. Calculate how many water blocks to fill based on water consumed
  2. Generate an array of boolean values representing filled/unfilled blocks
  3. Track when the last drink was taken

These values are perfect for $derived because they:

  • Are pure calculations based on state
  • Need to update automatically when dependencies change
  • Don't perform side effects

Understanding $effect

$effect is for running side effects when reactive values change. Unlike $derived, it doesn't return a value - it performs actions.

From our app:

1$effect(() => {
2	// Persist data to localStorage whenever values change
3	if (!firstRun) {
4		localStorage.setItem(
5			'waterData',
6			JSON.stringify({
7				waterConsumedSaved: waterConsumed,
8				lastDrinkSaved: lastDrink,
9				dailyGoalSaved: dailyGoal
10			})
11		);
12	} else {
13		const waterData = localStorage.getItem('waterData');
14		if (waterData) {
15			const { waterConsumedSaved, lastDrinkSaved, dailyGoalSaved } = JSON.parse(waterData);
16			waterConsumed = waterConsumedSaved;
17			lastDrink = lastDrinkSaved ? new Date(lastDrinkSaved) : null;
18			dailyGoal = dailyGoalSaved;
19		}
20		firstRun = false;
21	}
22});


This $effect is perfect for what it's doing:

  • Persisting data to localStorage (side effect)
  • Loading data on first run (side effect)
  • Tracking when state changes to trigger these operations

When to Use $effect (Sparingly)

$effect should be used rarely and only for side effects. Common appropriate uses include:

  1. Persisting data to localStorage (as in our example)
  2. Making API calls when dependencies change
  3. Interacting with the DOM in ways not handled by Svelte's templating
  4. Logging or analytics

Common $effect Misuses

Here are examples of misusing $effect that should be avoided:

Misuse 1: Computing values that should be $derived

// BAD
let doubledWater = 0;
$effect(() => {
  doubledWater = waterConsumed * 2;
});

// GOOD
const doubledWater = $derived(waterConsumed * 2);

Misuse 2: Conditional side effects that could be inline

// BAD
$effect(() => {
  if (waterConsumed >= dailyGoal) {
    console.log('Goal reached!');
  }
});

// GOOD - In template
{#if waterConsumed >= dailyGoal}
  <div on:mount={() => console.log('Goal reached!')}>Goal reached!</div>
{/if}

Misuse 3: Overusing for simple DOM manipulations

$effect(() => {
  document.title = `Water: ${waterConsumed}ml`;
});

// GOOD - Use Svelte's <svelte:head> component instead

Conclusion

Understanding the difference between $derived and $effect is crucial:

  • $derived is for computing values based on state (pure, returns a value)
  • $effect is for running side effects when state changes (impure, no return value)

By using the right rune for the job, your Svelte 5 applications will be more maintainable, predictable, and efficient. Our water tracker app demonstrates how these runes can work together to create a responsive, state-driven application.

Written by...
Mike Karan

I've been a full-stack web developer for 8+ years! Co-host of the HTML All The Things Podcast. I love all things technology.

More to Read...