Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Derived values: $derived

Often one value depends entirely on another: the number of remaining tasks on the list of tasks, the total price on the quantity and the price. Such values are not stored separately but computed with the $derived rune. Svelte recalculates them when the data they come from changes.

The $derived rune

<!-- src/routes/+page.svelte -->
<script>
  let todos = $state([
    { id: 1, text: 'Buy bread', done: true },
    { id: 2, text: 'Call Oleg', done: false }
  ]);
 
  let remaining = $derived(todos.filter((todo) => !todo.done).length);
</script>
 
<p>Tasks left: {remaining}</p>
<button onclick={() => (todos[1].done = true)}>Finish the second</button>

At first the page shows "Tasks left: 1", after the click — "Tasks left: 0". You do not recalculate remaining by hand: Svelte sees that the expression reads todos and updates the result whenever the list changes.

Chained calculations

A derived value can depend on another derived value:

<script>
  let todos = $state([
    { id: 1, text: 'Buy bread', done: true },
    { id: 2, text: 'Call Oleg', done: false }
  ]);
 
  let doneCount = $derived(todos.filter((todo) => todo.done).length);
  let percent = $derived(Math.round((doneCount / todos.length) * 100));
</script>
 
<p>Done: {percent}%</p>

The page shows "Done: 50%". If you mark the second task, doneCount is recalculated first, then percent, and the text becomes "Done: 100%".

Complex calculations: $derived.by

$derived takes a single expression. If the calculation needs several lines, use $derived.by with a function:

<script>
  let todos = $state([
    { id: 1, text: 'Buy bread', done: true },
    { id: 2, text: 'Call Oleg', done: false }
  ]);
  let filter = $state('all');
 
  let visible = $derived.by(() => {
    if (filter === 'active') return todos.filter((todo) => !todo.done);
    if (filter === 'done') return todos.filter((todo) => todo.done);
    return todos;
  });
</script>
 
<button onclick={() => (filter = 'active')}>Active</button>
<p>Tasks shown: {visible.length}</p>

At first two tasks are shown, after clicking "Active" — one. The value depends on both todos and filter: a change in either one triggers a recalculation.

$derived or a function

You could write a getRemaining() function and call it in the markup. But a function runs again in every place where it is called, while $derived is computed once, remembers the result and recomputes it only when its dependencies have changed. It is also read like a regular variable — both in the markup and in the script.

Common mistake

Storing a derived value in $state and updating it by hand:

<script>
  let todos = $state([]);
  let remaining = $state(0);
 
  function addTodo(text) {
    todos.push({ id: Date.now(), text, done: false });
    remaining = remaining + 1;
  }
</script>

Forget to update remaining in one place — for example, when a task is deleted — and the number on the page no longer matches the list. Anything computed from other state should be declared with $derived.

Thanks for reading!

Donate to the site — we work hard for you.