Svelte and SvelteKit course · Module 3: Reactivity and Runes

$derived - Derived Values

2 min read ·
In this lesson5

$derived creates a value that automatically updates when its dependencies change. It is like a rune that reacts to other runes!

Basic Usage

<script>
  let warriors = $state(10);
  let ships = $state(2);

  // Automatically updates
  let totalPower = $derived(warriors * 10 + ships * 50);
  let armyStatus = $derived(
    totalPower > 200 ? 'Mighty army!' : 'Need more warriors'
  );
</script>

<p>Warriors: {warriors}, Ships: {ships}</p>
<p>Power: {totalPower}</p>
<p>Status: {armyStatus}</p>

$derived vs $state

<script>
  let a = $state(5);
  let b = $state(3);

  // Do NOT do this - manual synchronization
  let sumWrong = $state(a + b);
  // sumWrong will not update when a or b changes!

  // Use $derived
  let sumRight = $derived(a + b);
  // sumRight will always be up to date!
</script>

Complex Computations

<script>
  let items = $state([
    { name: 'Sword', value: 100, count: 2 },
    { name: 'Shield', value: 50, count: 5 },
    { name: 'Helmet', value: 75, count: 3 }
  ]);

  // Array computations
  let totalItems = $derived(
    items.reduce((sum, item) => sum + item.count, 0)
  );

  let totalValue = $derived(
    items.reduce((sum, item) => sum + item.value * item.count, 0)
  );

  let mostValuable = $derived(
    items.reduce((max, item) =>
      item.value > max.value ? item : max
    )
  );
</script>

<p>Items: {totalItems}</p>
<p>Value: {totalValue} gold</p>
<p>Most valuable: {mostValuable.name}</p>

$derived.by - Complex Logic

For more complex computations use $derived.by:

<script>
  let warriors = $state([
    { name: 'Ragnar', strength: 95, isAlive: true },
    { name: 'Bjorn', strength: 88, isAlive: true },
    { name: 'Ivar', strength: 70, isAlive: false }
  ]);

  // Complex logic with multiple steps
  let battleReport = $derived.by(() => {
    const alive = warriors.filter(w => w.isAlive);
    const totalStrength = alive.reduce((sum, w) => sum + w.strength, 0);
    const avgStrength = alive.length > 0
      ? totalStrength / alive.length
      : 0;

    return {
      aliveCount: alive.length,
      totalStrength,
      avgStrength: Math.round(avgStrength),
      strongest: alive.sort((a, b) => b.strength - a.strength)[0]?.name ?? 'None'
    };
  });
</script>

<div class="report">
  <h3>Battle Report</h3>
  <p>Alive: {battleReport.aliveCount}</p>
  <p>Total strength: {battleReport.totalStrength}</p>
  <p>Average strength: {battleReport.avgStrength}</p>
  <p>Strongest: {battleReport.strongest}</p>
</div>

Derived Chain

<script>
  let baseValue = $state(10);

  // Each derived depends on the previous one
  let doubled = $derived(baseValue * 2);      // 20
  let withBonus = $derived(doubled + 5);       // 25
  let final = $derived(withBonus * 1.1);       // 27.5
</script>

Derived values form a chain of rune magic!

Code for this lesson: App.svelte
<script>
  // $derived - derived values

  let items = $state([
    { name: 'Sword', value: 100, count: 2 },
    { name: 'Shield', value: 50, count: 5 },
    { name: 'Helmet', value: 75, count: 3 }
  ]);

  // Simple derived
  let totalItems = $derived(
    items.reduce((sum, i) => sum + i.count, 0)
  );

  let totalValue = $derived(
    items.reduce((sum, i) => sum + i.value * i.count, 0)
  );

  // $derived.by for complex logic
  let report = $derived.by(() => {
    const total = items.reduce((sum, i) => sum + i.count, 0);
    const value = items.reduce((sum, i) => sum + i.value * i.count, 0);
    const mostValuable = items.reduce((max, i) =>
      i.value > max.value ? i : max
    );

    return {
      total,
      value,
      average: total > 0 ? Math.round(value / total) : 0,
      mostValuable: mostValuable.name
    };
  });

  function addItem() {
    items.push({ name: 'Axe', value: 80, count: 1 });
  }
</script>

<main class="treasury">
  <h1>Treasury</h1>

  <section>
    <h2>Items</h2>
    <table>
      <thead>
        <tr>
          <th>Item</th>
          <th>Value</th>
          <th>Quantity</th>
          <th>Total</th>
        </tr>
      </thead>
      <tbody>
        {#each items as item}
          <tr>
            <td>{item.name}</td>
            <td>{item.value}</td>
            <td>{item.count}</td>
            <td>{item.value * item.count}</td>
          </tr>
        {/each}
      </tbody>
    </table>
    <button onclick={addItem}>+ Add Axe</button>
  </section>

  <section class="summary">
    <h2>Summary ($derived)</h2>
    <div class="stats">
      <div class="stat">
        <span class="label">Items</span>
        <span class="value">{totalItems}</span>
      </div>
      <div class="stat">
        <span class="label">Value</span>
        <span class="value">{totalValue}</span>
      </div>
    </div>
  </section>

  <section class="report">
    <h2>Report ($derived.by)</h2>
    <p>Total: <strong>{report.total}</strong> items</p>
    <p>Value: <strong>{report.value}</strong></p>
    <p>Average: <strong>{report.average}</strong>/pc</p>
    <p>Most valuable: <strong>{report.mostValuable}</strong></p>
  </section>
</main>

<style>
  .treasury {
    font-family: 'Georgia', serif;
    max-width: 600px;
    margin: 0 auto;
    padding: 2rem;
    background: #1a0f0a;
    color: #e8d5b7;
  }

  h1, h2 {
    color: #d4af37;
  }

  section {
    background: rgba(212, 175, 55, 0.1);
    border: 1px solid #d4af37;
    border-radius: 8px;
    padding: 1rem;
    margin-bottom: 1rem;
  }

  table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 1rem;
  }

  th, td {
    padding: 0.5rem;
    text-align: left;
    border-bottom: 1px solid #3d2914;
  }

  th {
    color: #d4af37;
  }

  button {
    padding: 0.5rem 1rem;
    background: #3d2914;
    color: #e8d5b7;
    border: 1px solid #d4af37;
    border-radius: 4px;
    cursor: pointer;
  }

  .stats {
    display: flex;
    gap: 2rem;
  }

  .stat {
    text-align: center;
  }

  .stat .label {
    display: block;
    color: #8b7355;
    font-size: 0.9rem;
  }

  .stat .value {
    font-size: 1.5rem;
    color: #d4af37;
  }

  strong {
    color: #d4af37;
  }
</style>

Docs entries used in this lesson

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What is $derived used for?

  2. 2. When should you use $derived.by instead of $derived?

Hands-on tasks in the game

  • Code editor

    Complete the code so that doubled is twice the value of count

Useful articles