Svelte 5: How runes change the game

Published on 12/23/2023

svelte

Svelte 5 brings new changes, let's see them and how they work

diagram props drilling


Svelte 5 introduces a suite of new features and improvements, including the innovative use of runes for state management. Let’s explore these changes and understand how they elevate your Svelte development experience.


$state rune


One first big change is runes, the subject of this article; runes are the new way to declare state in svelte. We were used to declare state just like a regular javascript variable assignment, instead now we use a rune, which is an helper function, $state()


For example let’s create a basic counter and see the differences between the two approaches


The old one:


<script>
    let counter = 0;
</script>

<button on:click={() => counter -= 1}>Decrease</button>
<h1>{counter}</h1>
<button on:click={() => counter += 1}>Increase</button>

The new one:


<script>
    let count = $state(0);
</script>

<button onclick={() => count-=1}>Decrease</button>
<h1>{count}</h1>
<button onclick={() => count+=1}>Increase</button>

At first look this change can seem negative, but this isn’t the case: the new $state runes not only makes clear which state is reactive and which is not but also allows reactive expressions to work inside of functions and in .js files, removing the need for stores.


Let’s see how we can use runes in functions to replace stores:


<script lang="ts">function createCounter() {
  let count2 = $state(0);
  return {
    get count() {
      return count2;
    },
    increment: () => count2 += 1
  };
}
const count = createCounter();
</script>

{count.count}
<button on:click={count.increment}>Increment</button>

As you can see we are returning a getter function not directly count because it would return the value of count when the function is invoked not every time it changes.


$effect rune


In the previus versions of svelte to create reactive declarations we would use the $: sign, which worked but could easily mess things up: Let’ see this example to understand what i mean


<script lang="ts">let w = 100;
let h = 200;
let area;
$:
  area = w * h;
</script>

In this example we want to recalculate the area whenever the height or the width change; in this case everithing works as it should until it doesn’t; what if we changed the code like this:


<script lang="ts">let w = 100;
let h = 200;
let area = w * h;
const multiplyByHeight = (width) => width * h;
$:
  area = multiplyByHeight(w);
</script>

In this scenario the svelte compiler is sucessfully able to track down the width because is the function argument but not the height. What this result in is that when height changes, area is not recalculated, which is something we don’t want.


To solve this problem svelte 5 brings to new runes $derived and $effect, the last one with a doubt inspired by react.


What this two runes do is execute something when any of the dependency chenges, just like the $: but in a more predictable way. Let’s see them in action.


<script lang="ts">let w = $state(100);
let h = $state(200);
let area = $derived(w * h);
</script>

we can achieve the same result with the $effect rune:


<script lang="ts">let w = $state(100);
let h = $state(200);
let area = $state(0);
$effect(() => {
  area = w * h;
});
</script>

The $effect rune, replaces lifecycle functions such as afterUpdate .


$props rune


The last rune I want to talk about is $props. As the name suggests this rune is the new way to access to props. In the old versions you would have done something like this:
<script>
  export let propName
</script>

<h1>{propName}</h1>

In the new version you can invoke the $props rune like this:
<script>
 const {propName} = $props()
</script>

<h1>{propName}</h1>

In my opinion this is the best change in this update, because I never liked the old props syntax: it was unclear and was a weird way of using js export. With this new approach the code is very clear, explicit and elegant.


At the time of this article version 5 is still in a testing phase and is not stable. You can still try these features by specifying svelte@latest when creating the project.


For more information you can always check the official docs and the sveltekit blog


Conclusion


Svelte version 5 marks a significant evolution in the framework, introducing new features that change the developer experience and offer more control over reactivity and state management. The introduction of $state and $effect runes revolutionizes the way developers handle reactive states, making the code more intuitive and maintainable. Even though these changes can seem negative and “anti-svelte”, they not only simplify the process of state management but also improve the predictability and efficiency of reactivity within applications.


As we’ve explored, the shift from the traditional JavaScript variable assignment to the $state rune for declaring state, and the transition from the $: , syntax which was unclear and too abstract, to the more predictable $effect rune for reactive declarations, represent a considerable advancement in Svelte’s development path. These improvements demonstrate Svelte’s interest to be a player in the framework game.

While Svelte 5 is still in the testing phase at the time of writing, its potential impact on the future of web development is undeniable and the benefits of these changes will be clear as soon as people start to use them. Developers looking to stay ahead in the rapidly evolving world of web development should certainly keep an eye on these developments and consider experimenting with these new features in their projects.