Svelte's Reactivity

In this module, we will go over how Svelte's reactivity works, covering some of the Svelte basics along the way. We will start off by creating reactive assignments, followed by reactive declarations, and then reactive statements.
Table of Contents
Svelte's ReactivityView the code for this module.

In this module, we will go over how Svelte's reactivity works, covering some of the Svelte basics along the way. We will start off by creating reactive assignments, followed by reactive declarations, and finally we will learn how to use reactive statements. Keeping the DOM in sync with an app's state is one of the most important features of a modern frontend framework, and Svelte achieves this in a slick and unique way. Let's dive into how reactivity works with Svelte.

In the last module we created a new SvelteKit skeleton app, which we will be working out of today. To run this project locally, run the command npm run dev in the terminal. This will allow you to see all the changes you make real time. Let’s start by adding some static HTML to our root page.

<div>Your shopping cart has 5 items.</div>

Here, we are displaying our shopping cart quantity within a <div>. In this case, our quantity will always be five, since it is a hard coded value. To make this dynamic, we need to create a reactive assignment. To do this, we need to add a <script> tag, and define a variable quantity using let. We can then display this variable in our template using single curly braces. Our updated code will look like this.

<script>
  let quantity = 0;
</script>
 
<div>Your shopping cart has {quantity} items.</div>

Now, for the fun part – let’s make this variable reactive. We can add a button to our HTML that will call a function addToCart anytime it is clicked. This function will increment quantity by one.

<script>
  let quantity = 0;
  function addToCart() {
    quantity = ++quantity;
  }
</script>
 
<div>Your shopping cart has {quantity} items.</div>
<button on:click="{addToCart}">Add To Cart</button>

If you run this in the browser and test it out, you will see each time the value of quantity changes, our HTML is automatically updated with the new value. Svelte almost feels like magic here; It doesn’t use any special APIs to re-render our UI – it uses normal javascript assignment through the equals sign to notify our UI to update. This is the core idea behind how Svelte’s reactivity system works.

Now, let’s talk about arrays.

<script>
  let quantity = 0;
  let inventory = [];
  function addToCart() {
    inventory.push(quantity);
    inventory = inventory;
    quantity = ++quantity;
  }
  let inventory = [];
</script>
 
<div>Your shopping cart has {quantity} items.</div>
<button on:click="{addToCart}">Add To Cart</button>
<div>{inventory}</div>

In the above code we’ve created a new variable, inventory, which will start off as an empty array. Next, in our addToCart function, prior to incrementing quantity we are adding it to this array. Notice after pushing quantity to our array, I write inventory = inventory. This may seem a little redundant as it may be tempting to call inventory.push(quantity) – but if you were to test this out in the browser, you would see that our inventory array is not updating. This is because the Array.push method in Javascript actually mutates an existing array, but it leaves the overall Array object itself unchanged. To actually re-render our app we need to make sure to always use the assignment operator. Adding inventory = inventory on the next line is necessary to trigger an update. The general takeaway here is, if you want to update a reactive variable, always use the equals sign.

Assignments to properties of arrays and objects work the same way as assignments to the values themselves. Let’s look at an example of changing the property of an object.

<script>
  let user = { name: 'Steph' };
  function updateUser() {
    user.name = 'Lee';
  }
</script>
 
<button on:click="{updateUser}">Update User</button>
<div>{user.name}</div>

In this example, we can update the value of name by writing user.name = . In the above example, you will see the template update with the new name, 'Lee', when the button is clicked. As long as we’re referencing a value that gets reassigned through the assignment operator, the template will update. But if we ever want to update an entire Object or Array, we’ll need to make sure to remember to update the entire thing, rather than using one of Javascript’s mutation-based methods like Array.push.

Svelte's reactivity is not limited to variable assignments. Often, some parts of a component's state need to be computed from other parts. For example, let's write the following:

<script>
  let quantity = 0;
  function addToCart() {
    quantity = ++quantity;
  }
  let remaining = 10 - quantity;
</script>
 
<button on:click="{addToCart}">Add To Cart</button>
<div>Remaining Inventory: {remaining}</div>

Here, we are displaying our new variable, remaining, in the HTML, which is between 10 and our quantity value. We would expect this value to change as our quantity value is incremented, but if we click addToCart, we see that remaining is always 10, even though the value of quantity is changing. This is because remaining is not being recomputed as quantity changes. In order to recompute remaining anytime quantity changes, we need to use reactive declarations, which are marked using a $:. They look like this:

<script>
  let quantity = 0;
  function addToCart() {
    quantity = ++quantity;
  }
  $: remaining = 10 - quantity;
</script>
 
<button on:click="{addToCart}">Add To Cart</button>
<div>Remaining Inventory: {remaining}</div>

Now, remaining is reactive and will be recomputed anytime quantity changes. The syntax here may seem a bit unfamiliar, but it's valid JavaScript, which Svelte interprets to mean 're-run this code whenever any of the referenced values change'. Reactive statements can use as many variables as you'd like. For example, let's add another variable called price and a reactive statement called totalCost that multiplies our quantity and price.

<script>
  let quantity = 0;
  function addToCart() {
    quantity = ++quantity;
  }
  $: remaining = 10 - quantity;
  let price = 5;
  $: totalCost = quantity * price;
  function increasePrice() {
    price = price + 2;
  }
</script>
 
<button on:click="{addToCart}">Add To Cart</button>
<div>Remaining Inventory: {remaining}</div>
<div>Item Price: {price}</div>
<button on:click="{increasePrice}">Increase Price</button>
<div>Total Cost: {totalCost}</div>

Now, totalCost will update anytime quantity changes, and also anytime price changes.

We're not limited to declaring reactive values — we can also run statements reactively. For example, once again using the $: syntax, we can log the value of quantity whenever it changes:

<script>
  let quantity = 0;
  let inventory = [];
  function addToCart() {
    inventory.push(quantity);
    inventory = inventory;
    quantity = ++quantity;
  }
  $: console.log(`the quantity is ${quantity}`);
</script>
 
<div>Your shopping cart has {quantity} items.</div>
<button on:click="{addToCart}">Add To Cart</button>

In the above example, every time the value of quantity is changed, this statement runs, and quantity is logged in our console. We can even group statements within a block like this:

<script>
  let quantity = 0;
  function addToCart() {
    inventory.push(quantity);
    inventory = inventory;
    quantity = ++quantity;
  }
  let inventory = [];
  let user = { name: 'Steph' };
  $: remaining = 10 - quantity;
  $: console.log(`the quantity is ${quantity}`);
  $: {
    console.log(`the quantity is ${quantity}`);
    alert(`There are ${remaining} products remaining`);
  }
</script>
 
<div>Your shopping cart has {quantity} items.</div>
<button on:click="{addToCart}">Add To Cart</button>

Now, this entire block of code will run anytime a referenced value changes. We can even take this one step further and put the $: in front of things like if blocks:

<script>
  let quantity = 0;
  function addToCart() {
    inventory.push(quantity);
    inventory = inventory;
    quantity = ++quantity;
  }
  let inventory = [];
  let user = { name: 'Steph' };
  $: remaining = 10 - quantity;
  $: console.log(`the quantity is ${quantity}`);
  $: {
    console.log(`the quantity is ${quantity}`);
    alert(`There are ${remaining} products remaining`);
  }
  $: if (quantity >= 10) {
    alert(`You have too many items in your cart!`);
    quantity = 9;
  }
</script>
 
<div>Your shopping cart has {quantity} items.</div>
<button on:click="{addToCart}">Add To Cart</button>

Now, every time quantity changes Svelte will check if it is greater than or equal to 10. If it is, it will execute the statements within our block.

Now that we understand how reactivity works in Svelte, as well as a little Svelte-specific syntax, let’s continue in the next module with element directives.