From a0356e14aabc2269bdcf86bafebe8ce5eb376eab Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 25 Feb 2026 12:01:34 -0500 Subject: [PATCH] updates --- .../docs/07-misc/01-best-practices.md | 182 ++++++++---------- 1 file changed, 80 insertions(+), 102 deletions(-) diff --git a/documentation/docs/07-misc/01-best-practices.md b/documentation/docs/07-misc/01-best-practices.md index 262055b5d7..0fc9d3707c 100644 --- a/documentation/docs/07-misc/01-best-practices.md +++ b/documentation/docs/07-misc/01-best-practices.md @@ -2,166 +2,144 @@ title: Best practices --- -This document is meant to collect a series of best practices to write not only correct but good Svelte code. The content of this page was originally created as a `SKILL.md` file but given it's content it can (and should) be used as a reference by human developers that wish to write the best possible Svelte code. +This document outlines some best practices that will help you write fast, robust Svelte apps. It is also available as a `svelte-best-practices` skill for your agents. ->[!NOTE] This document will be also synchronized with the [mcp repository](https://github.com/sveltejs/mcp) to serve as a SKILL. To follow the rules of progressive discovery a few paragraph of this page that are only useful in specific situations during svelte development will merely link to other sections of the documentation. +## `$state` -## `$state` and `$derived` +Only use the `$state` rune for variables that should be _reactive_ — in other words, variables that cause an `$effect`, `$derived` or template expression to update. Everything else can be a normal variable. -When writing a Svelte component, each variable used inside an `$effect`, `$derived` or in the template must be declared with `$state`. Objects and arrays are automatically deeply reactive, and reactivity can be triggered via mutation. If you don't need mutations triggering reactivity, consider using `$state.raw` to improve performance. Not every variable must be stateful; if a variable is never used in an `$effect`, `$derived` or the template you can avoid using `$state` completely. +Objects and arrays (`$state({...})` or `$state([...])`) are made deeply reactive, meaning mutation will trigger updates. This has a trade-off: in exchange for fine-grained reactivity, the objects must be proxied, which has performance overhead. In cases where you're dealing with large objects that are only ever reassigned (rather than mutated), use `$state.raw` instead. This is often the case with API responses, for example. -If one stateful variable depends on another stateful variable, you must use `$derived` to create this new piece of state. `$derived` accepts an expression as input. If you want to use a function, you must use `$derived.by`. Only the stateful variables that are read within a derived actually count as a dependency of that derived. This means that if you guard the read of a stateful variable with an `if`, that stateful variable will only be a dependency when the condition is true. The value of a derived can be overridden; When overridden, the value will change immediately and trigger a DOM update. But when one of the dependencies changes, the value will be recalculated and the DOM updated once again. If a component is receiving a prop and you want a piece of state to be initialised from that prop, usually it's a good idea to use a derived because if that prop is a stateful variable, when it changes, Svelte will just update the value, not remount the component; If the value could be an object, a class, or an array, the suggestion is to use `$state.snapshot` to clone them (`$state.snapshot` is using `structuredClone` under the hood so it might not always be a good idea). +## `$derived` -## The `$effect` rune +To compute something from state, use `$derived` rather than `$effect`: -`$effect` is generally considered a malpractice in Svelte. You should almost never use `$effect` to sync between stateful variables (use a `$derived` for that) and reassigning state within an `$effect` is especially bad. When encountering an `$effect` asks yourself if that's really needed. It can usually be substituted by: +```js +// do this +let square = $derived(num * num); -- A `$derived` -- An `@attach` -- A class that uses `createSubscriber` +// don't do this +let square; -The valid use cases for `$effect` are mainly to sync Svelte reactivity with a non-reactive system (like a D3 class, `localStorage`, or inside an attachment to do imperative DOM operations). - -Like `$derived`, `$effect` automatically has every stateful variable (declared with `$state` or `$derived`) as a dependency when it's read (if you guard the read of a stateful variable with an `if`, that stateful variable will only be a dependency when the condition is true) - -If you want to log a value whenever the reactive variable changes use `$inspect` instead. - -For more information on when not to use `$effect` read [this document](/docs/svelte/$effect#When-not-to-use-$effect). - -`$effect` only runs on the client so you don't need to guard with [`browser`](/docs/kit/$app-environment#browser) or `typeof window === "undefined"` - -## `$bindable` - -You can use `$bindable` inside `MyComponent.svelte` like this - -```svelte - +$effect(() => { + square = num * num; +}); ``` -to allow ``. This can get hairy when the value of the prop is not a literal value; try to use callbacks in that case. +> [!NOTE] `$derived` is given an expression, _not_ a function. If you need to use a function (because the expression is complex, for example) use `$derived.by`. -```svelte - -``` +Deriveds are writable — you can assign to them, just like `$state`, except that they will re-evaluate when their expression changes. -## `$inspect.trace` +If the derived expression is an object or array, it will be returned as-is — it is _not_ made deeply reactive. You can, however, use `$state` inside `$derived.by` in the rare cases that you need this. -`$inspect.trace` is a debugging tool for reactivity. If something is not updating properly or running more than it should you can put `$inspect.trace("[yourlabel]")` as the first line of an `$effect` or `$derived.by` to get detailed logs about the dependencies of it. +## `$effect` -## Events on elements +Effects are an escape hatch and should mostly be avoided. In particular, avoid writing state inside effects. -Every prop that starts with `on` is treated as an event listener for the element. To register a `click` listener on an element you can do `