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.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.
Was this helpful?