Sobald du einen Wert hast, der sich aus anderen reaktiven Werten berechnen lässt, gehört er in $derived. Eine Summe, eine gefilterte Liste, ein voller Name aus Vor- und Nachname — überall dort, wo das Ergebnis automatisch dem Stand der Quellen folgen soll, ist $derived die richtige Wahl. Im Gegensatz zu Reacts useMemo musst du keine Abhängigkeiten auflisten — der Compiler beobachtet von selbst, welche reaktiven Werte du im Ausdruck liest.
Grundprinzip
<script>
let count = $state(0);
let doubled = $derived(count * 2);
</script>
<button onclick={() => count++}>+1</button>
<p>{count} ⇒ {doubled}</p>Der Trick: doubled ist kein weiterer State, sondern eine Formel. Du kannst nicht direkt doubled = 99 schreiben — der Wert ergibt sich immer aus count. Sobald count sich ändert, gibt der Compiler doubled als „veraltet” aus, beim nächsten Lesen wird die Multiplikation neu ausgeführt.
Das ist mehr als Komfort: Es schließt eine ganze Klasse von Bugs aus. In klassischem Code könnte man vergessen, doubled mit zu aktualisieren, wenn count sich ändert — die zwei Werte würden auseinanderlaufen. Mit $derived kann das nicht mehr passieren.
Mehrere Abhängigkeiten — automatisch erkannt
Anders als bei Reacts useMemo([a, b]) musst du in $derived keine Abhängigkeiten deklarieren. Der Compiler liest deinen Ausdruck und merkt sich, welche reaktiven Werte darin auftauchen. Beim Update einer dieser Werte wird die Berechnung als veraltet markiert.
<script>
let firstName = $state('Anna');
let lastName = $state('Schmidt');
let title = $state('Dr.');
let fullName = $derived(`${title} ${firstName} ${lastName}`);
</script>
<h1>{fullName}</h1>Sobald sich eine der drei Quellen ändert, wird fullName neu berechnet.
$derived.by für komplexere Logik
Die normale Form $derived(ausdruck) braucht einen Ausdruck. Sobald du eine Schleife, ein if, oder Hilfsvariablen brauchst, wird das eng. Genau dafür gibt es die Funktions-Variante $derived.by(() => …) — du übergibst eine Funktion, die rechnet und am Ende den Wert zurückgibt.
<script>
let items = $state([
{ name: 'Brot', price: 2.5, qty: 2 },
{ name: 'Milch', price: 1.2, qty: 1 },
]);
let summary = $derived.by(() => {
let total = 0;
let count = 0;
for (const item of items) {
total += item.price * item.qty;
count += item.qty;
}
return { total, count };
});
</script>
<p>{summary.count} Artikel, gesamt {summary.total.toFixed(2)} €</p>Die Funktion läuft genau dann neu, wenn sich eine der darin gelesenen reaktiven Quellen ändert. Die Tracking-Logik ist identisch zur einfachen Variante — nur der Body darf jetzt mehrere Anweisungen haben.
Lazy und gecacht: warum teure Berechnungen kein Problem sind
Zwei Eigenschaften von $derived machen es performance-freundlich, ohne dass du selbst optimieren musst.
Lazy. Die Berechnung läuft erst, wenn jemand den Wert tatsächlich liest — etwa weil das Markup ihn anzeigt oder ein Effect ihn nutzt. Definierst du einen $derived, ohne ihn irgendwo abzurufen, passiert gar nichts. Das heißt: Auch eine teure Ableitung ist unbedenklich, solange sie nicht angezeigt wird.
Gecacht. Wird der Wert mehrfach in derselben Render-Phase gelesen, läuft die Berechnung trotzdem nur einmal. Erst nach einer Änderung der Quellen wird beim nächsten Lesen wieder gerechnet.
let items = $state([1, 2, 3, 4, 5]);
let sum = $derived.by(() => {
console.log('compute');
return items.reduce((a, b) => a + b, 0);
});
// Mehrere Reads — nur ein "compute" pro Änderung
const a = sum;
const b = sum;
const c = sum;Vergleich zu Svelte 4 ($:)
In Svelte 4 wurden Ableitungen mit dem reaktiven Label $: geschrieben:
<script>
let count = 0;
$: doubled = count * 2;
$: tripled = count * 3;
</script>In Svelte 5 wird daraus:
<script>
let count = $state(0);
let doubled = $derived(count * 2);
let tripled = $derived(count * 3);
</script>Der zentrale Unterschied: $: verband Ableitungen und Side Effects in derselben Syntax. $derived ist klar als „pure Ableitung” markiert — Side Effects gehören in $effect. Diese Trennung führt zu deutlich vorhersehbarerem Verhalten.
Praxis-Beispiele
Filter & Sort
<script>
let users = $state([...]);
let query = $state('');
let sortBy = $state('name');
let visible = $derived(
users
.filter((u) => u.name.toLowerCase().includes(query.toLowerCase()))
.toSorted((a, b) => a[sortBy].localeCompare(b[sortBy]))
);
</script>Validierung
<script>
let email = $state('');
let password = $state('');
let errors = $derived.by(() => {
const result = {};
if (!email.includes('@')) result.email = 'Ungültige E-Mail';
if (password.length < 8) result.password = 'Mindestens 8 Zeichen';
return result;
});
let isValid = $derived(Object.keys(errors).length === 0);
</script>
<input bind:value={email} />
{#if errors.email}<small>{errors.email}</small>{/if}
<input type="password" bind:value={password} />
{#if errors.password}<small>{errors.password}</small>{/if}
<button disabled={!isValid}>Abschicken</button>Abgeleitete Klassen-Properties
export class Cart {
items = $state<CartItem[]>([]);
total = $derived(
this.items.reduce((sum, i) => sum + i.price * i.qty, 0)
);
count = $derived(
this.items.reduce((c, i) => c + i.qty, 0)
);
}Häufige Stolperfallen
$state statt $derived für Berechnungen.
Berechnete Werte gehören in $derived, sonst geraten sie out-of-sync.
Side Effects in $derived.
$derived soll pure sein — keine console.log, keine API-Calls, kein DOM-Schreiben. Lazy & memoiziert bedeutet: Die Funktion läuft möglicherweise nicht oder mehrfach hintereinander. Side Effects gehören in $effect.
$derived mit nicht-reaktiver Variable.
let count = 0; // kein $state!
let doubled = $derived(count * 2); // wird nie aktualisiert$derived reagiert nur auf reaktive Quellen.
Asynchrone Logik in $derived.
$derived muss synchron einen Wert zurückgeben. Für asynchrone Flows passen Promises mit {#await} oder ein $state(null) plus $effect mit Fetch.