Reactivity in Svelte
Tags: svelte, javascript
Published: 2021-12-27 09:20:45

---
title: Reactivity in Svelte
published: true
description: Keeping your application in sync with its state is one of the most important features that a framework can provide. In this post, we'll learn about how reactivity works in Svelte and avoid common issues when using it.
tags: svelte, javascript
cover_image: https://images.ctfassets.net/zojzzdop0fzx/40vrjk1OBm2uIRTCrr3ehX/ccbfdef6bfb36834429cbf78c9fe8127/Handling_Forms_in_Svelte__1_.png
canonical_url: https://www.thisdot.co/blog/reactivity-in-svelte
---

Keeping your application in sync with its state is one of the most important features that a framework can provide. In this post, we'll learn about how reactivity works in Svelte, and avoid common issues when using it.

Let's start a new application to explain how it works.

```bash
npm init @vitejs/app

✔ Project name: · svelte-reactivity
✔ Select a framework: · svelte
✔ Select a variant: · svelte-ts

cd svelte-reactivity
pnpm install //use the package manager you prefer
pnpm run dev
```

We will remove everything we have in our `App.svelte` component an replace it with the following:

```html
<!-- App.svelte -->
<script lang="ts">
	let language: 'es'|'en' = 'en';

	function toggleLanguage() {
		language = language === 'en' ? 'es' : 'en';
	}
</script>

<main>
	<p>{language}</p>
	<button on:click={toggleLanguage}>Toggle Language</button>
</main>
```
![Reactivity_Svelte_01](//images.contentful.com/zojzzdop0fzx/759pTRUlUIWOBEfHrW45EM/5155709d9ccf5f2584b5936d2ee2b949/reactivity1.gif)

We added a button with an event handler responsible for toggling our variable with two values `en` and `es`. 
We can see that the value is updated every time we click the button.

In Svelte, the DOM is updated when an assignment is made. In this example, `language` is assigned with the result of `language === 'en' ? 'es' : 'en'`.
Behinds the scenes, Svelte will take care of rerendering the value of language when the assignment happens.

If we take a look at the compiled code we will find this.

```javascript
/* App.svelte generated by Svelte v3.38.3 */
// ...

function instance($$self, $$props, $$invalidate) {
	let language = "en";

	function toggleLanguage() {
		$$invalidate(0, language = language === "en" ? "es" : "en");
	}

	return [language, toggleLanguage];
}

// ...
```

We can see that our `toggleLanguage` function looks a bit different, wrapping the assignment with the `$$invalidate` method.

Let's make a few more changes to our file to see how assignment affects reactivity and rerendering.

```html
<!-- App.svelte -->
<script lang="ts">
	let testArray = [0]

	function pushToArray(){
		testArray.push(testArray.length)
	}

	function assignToArray(){
		testArray = [...testArray, testArray.length]
	}
</script>
<main>
	<p>{testArray}</p>
	<button on:click={pushToArray}>Push To Array</button>
	<button on:click={assignToArray}>Assign To Array</button>
</main>
```

![Reactivity_Svelte_02](//images.contentful.com/zojzzdop0fzx/1U1aJ103xK8rz3R3fvONFj/cdf8a055399cb0f94f67bd5d33529571/reactivity2.gif)

![Reactivity_Svelte_03](//images.contentful.com/zojzzdop0fzx/2Ymi3GdqMGB2N1bfQ54E3V/c06248a7e9658b9fe10002d81cea8dad/reactivity3.gif)

Whenever we click on the `Assign To Array` Button, the DOM is updated with the new value.
When we try to get the same result by mutating the array, the DOM is not updated, but the app state is. We can verify that when we later click the Assignment button and the DOM is updated, showing the actual state of `testArray`.

Let's inspect the generated code once again.

```javascript
function instance($$self, $$props, $$invalidate) {
	let testArray = [0];

	function pushToArray() {
		testArray.push(testArray.length);
	}

	function assignToArray() {
		$$invalidate(0, testArray = [...testArray, testArray.length]);
	}

	return [testArray, pushToArray, assignToArray];
}
```

If you compare both functions, we can now see that only the assignment will call the `$$invalidate` method, while the other one calls the expression as is.

This doesn't mean we cannot mutate arrays and force a rerender. We need to use an assignment after mutation to do it.

```html
<!-- App.svelte -->
<script lang="ts">
	//...

	function pushToArray(){
		testArray.push(testArray.length)
		testArray = testArray
	}

	//...
</script>
```

Our complied function will be updated to:
```javascript
function pushToArray() {
	testArray.push(testArray.length);
	$$invalidate(0, testArray);
}
```
which will update the DOM when called(`$$invalidate` method wraps the expression, that is simplified to `testArray` instead of `testArray = testArray`)

![Reactivity_Svelte_04](//images.contentful.com/zojzzdop0fzx/2xCj1Ff4AvKo7PKD2GcMWD/c88f1ce4b3e6c0862362b3a2a2946511/reactivity4.gif)

## Reactive Variables
Imagine our team decided that we need to add a second array where each value is squared. If we were doing it imperatively, this would mean we need to update the second array each time the first one changes.
The previous example would look like this.

```html
<!-- App.svelte -->
<script lang="ts">
	let testArray = [0]
	let squared = [0]

	function pushToArray(){
		testArray.push(testArray.length)
		testArray = testArray
		squared = testArray.map(value => value*value)
	}

	function assignToArray(){
		testArray = [...testArray, testArray.length]
		squared = testArray.map(value => value*value)
	}
</script>
<main>
	<p>{testArray}</p>
	<p>{squared}</p>
	<!-- ... -->
</main>
```
![Reactivity_Svelte_05](//images.contentful.com/zojzzdop0fzx/1wo7oeH3M8CVbwk6SkrKS7/6f8db54b1c0f97590caa25db8ade949f/reactivity5.gif)

If we check the generated code again, we'll see that we are invalidating both arrays every time.

```javascript
function pushToArray() {
	testArray.push(testArray.length);
	$$invalidate(0, testArray);
	$$invalidate(1, squared = testArray.map(value => value * value));
}

function assignToArray() {
	$$invalidate(0, testArray = [...testArray, testArray.length]);
	$$invalidate(1, squared = testArray.map(value => value * value));
}
```

Unfortunately, this approach has a problem. We need to keep track of every place where `testArray` is modified, and also update the `squared` array.

If we think about this problem reactively, we only need to listen to changes in `testArray`.

In Svelte, there's a special way to do this. Instead of declaring a variable with `let`, we will use `$:`. This is a *labeled statement* (it's valid JS), and it's used by the compiler to let it know that a reactive variable is being declared, and it depends on all the variables that are added to the expression.
In our example:

```html
<script lang="ts">
  let testArray = [0];
  $: squared = testArray.map(value => value * value)

  function pushToArray() {
    testArray.push(testArray.length);
    testArray = testArray;
  }

  function assignToArray() {
    testArray = [...testArray, testArray.length];
  }
</script>
```

Using this reactive approach, we need to handle changes to `testArray` exclusively.
The compiler will detect that there's a dependency in `testArray` to calculate the actual value of `squared`.

If you run the app again, the same behavior is achieved.

How did this happen?
Let's look at our compiled code.
```javascript
	$$self.$$.update = () => {
		if ($$self.$$.dirty & /*testArray*/ 1) {
			$: $$invalidate(1, squared = testArray.map(value => value * value));
		}
	};
```

The internal property `update` is now assigned to a function that will check if the instance has changed, and that invalidate `squared` if the condition is met.

Every other reactive variable we add to our component will add a new block that will check if a dependency changed, and invalidate the declared variable.
For example: 

```html
<script lang="ts">
  let testArray = [0];
  let multiplier = 5
  $: squared = testArray.map(value => value * value)
	// if ($$self.$$.dirty & /*testArray*/ 1) {
	//	 $: $$invalidate(1, squared = testArray.map(value => value * value));
	// }
  $: squaredTwice = squared.map(value => value * value)
	// if ($$self.$$.dirty & /*squared*/ 2) {
	//   $: squaredTwice = squared.map(value => value * value);
	// }
  $: multiplied: squaredTwice.map(value => value * multiplier)
	// if ($$self.$$.dirty & /*squaredTwice, multiplier*/ 34) {
	//   $: multiplied = squaredTwice.map(value => value * multiplier);
	// }

</script>
<!-- ... -->
```
The last declaration, however, depends on two variables, `squareTwice` and `multiplier`. You can tell by the comment in the `if` condition.

Our updated component now looks like this: 
```html
<script lang="ts">
  let testArray = [0];
  let multiplier = 5;

  $: squared = testArray.map((value) => value * value);
  $: squaredTwice = squared.map((value) => value * value);
  $: multiplied = squaredTwice.map((value) => value * multiplier);

  function pushToArray() {
    testArray.push(testArray.length);
    testArray = testArray;
  }

  function assignToArray() {
    testArray = [...testArray, testArray.length];
  }
</script>

<main>
  <p>{testArray}</p>
  <p>{squared}</p>
  <p>{squaredTwice}</p>
  <p>{multiplied}</p>
  <button on:click={pushToArray}>Push To Array</button>
  <button on:click={assignToArray}>Assign To Array</button>
  <button on:click={() => multiplier = multiplier + 1}>Multiplier</button>
</main>
```

I added a button to add 1 to `multiplier` to verify that the `multiplied` array is also depending on it.

![Reactivity_Svelte_06](//images.contentful.com/zojzzdop0fzx/aZVG8eAyK8hDeMUaHyM6A/f013b93d3ff1f46590a634b7fbb37a23/reactivity6.gif)

## Reactive Statements
Reactivity is not limited to variable declarations. Using the same `$:` pattern we can create reactive statements.
For example, we could add an if statement or add a try-catch block.

Let's try the following:

```html
<script lang="ts">
  //...
  let error = null;
  //...
  $: try {
    if (multiplier > 8) {
      throw 'boo';
    }
  } catch (e) {
    error = e;
  }
  //...
</script>

<main>
  <!-- ... -->
  {#if error}
    <p>{error}</p>
  {/if}
  <!-- ... -->
</main>
```

Looking at the generated code we can see the same pattern as before:
```javascript
if ($$self.$$.dirty & /*multiplier*/ 2) {
	$: try {
		if (multiplier > 8) {
			throw "boo";
		}
	} catch(e) {
		$$invalidate(4, error = e);
	}
}
```

The compiler recognizes how the statement depends on changes to `multiplier` and that invalidating `error` is a possibility.

## Store auto-subscription
A store is defined as an object that implements the following contract (at a minimum):
`store = { subscribe: (subscription: (value: any) => void) => (() => void), set?: (value: any) => void }`
Stores are beyond the scope of this post but they will make possible to listen for changes to a piece of your app state.
Then, we can translate this event (when the store emits a new value) into an assignment that, as we mentioned before, will update our DOM.
For example: 
```ts
// stores.ts
import { writable } from 'svelte/store';
export const storeArray = writable([0]);
```

```html
<!-- App.svelte -->
<script lang="ts">
  import { onDestroy } from 'svelte';
  import { storeArray } from './stores';

  let testArray;
  const unsubscribe = storeArray.subscribe((value) => {
    testArray = value;
  });
  function addValueToArray() {
    storeArray.update((value) => [...value, value.length]);
  }
  onDestroy(unsubscribe);
</script>

<main>
  <p>{testArray}</p>
  <button on:click={addValueToArray}>Add Value</button>
</main>

```

Whenever we `update`, or `set` our store, a new value will be emitted and assigned to `testArray`.

We can confirm that we are calling `$$invalidate` in the compiled code.
```javascript
const unsubscribe = storeArray.subscribe(value => {
		$$invalidate(0, testArray = value);
	});
```
![Reactivity_Svelte_08](//images.contentful.com/zojzzdop0fzx/LD2HCBrHgVi7drkWEMsnK/a01186f0d7da2d1ca1d85aec68f58854/reactivity_8.gif)

But there's another way to achieve this with auto-subscriptions.

Our component now becomes this:

```html
<script lang="ts">
  import { storeArray } from './stores';
  function addValueToArray() {
    storeArray.update((value) => [...value, value.length]);
  }
</script>

<main>
  <p>{$storeArray}</p>
  <button on:click={addValueToArray}>Add Value</button>
</main>
```

Looking at auto subscriptions. There's no assignment in it, but our DOM is updated when we update the array. How is this achieved?

Let's analyze the output code:

```javascript
function instance($$self, $$props, $$invalidate) {
	let $storeArray;
	component_subscribe($$self, storeArray, $$value => $$invalidate(0, $storeArray = $$value));

	function addValueToArray() {
		storeArray.update(value => [...value, value.length]);
	}

	return [$storeArray, addValueToArray];
}
```

We can see that we are calling `component_subscribe` with three parameters: the component, the store, and a callback function, which is invalidating our `$storeArray` variable.

If we go deeper and check what `component_subscribe` is doing underneath, we'll find the following:
```ts
export function subscribe(store, ...callbacks) {
	if (store == null) {
		return noop;
	}
	const unsub = store.subscribe(...callbacks);
	return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}

export function component_subscribe(component, store, callback) {
	component.$$.on_destroy.push(subscribe(store, callback));
}
```

... Which is doing the same as the original code.

It subscribes to the store, and returns a unsubscribe method (or an object with an unsubscribe method), and calls it when the component is destroyed. When a new value is emitted, the callback is executed (`$$invalidate`), assigning the emitted value to the auto-subscribe variable.

## Common issues
* Remember that we need an assignment to call `$$invalidate` mark the component instance as dirty and run all the checks.
`=, ++, --, +=, -=` are all considered assignments.

* When working with objects, the assignment must include the name of the variable referenced in the template.
	For example:
```html
<script>
  let foo = { bar: { baz: 1 } };
  let foo2 = foo;
  function addOne() {
    foo2.bar.baz++;
  }
  function refreshFoo() {
    foo = foo;
  }
</script>
<p>foo: {JSON.stringify(foo, null, 2)}</p>
<p>foo2: {JSON.stringify(foo2, null, 2)}</p>

<button on:click={addOne}> add 1 </button>

<button on:click={refreshFoo}> Refresh foo </button>
```

When adding 1 to `foo2.bar.baz` the compiler only knows that it must update references to `foo2` in the templates, but it will not update references to `foo` event if it changes too (they're the same object). When calling `refreshFoo` we are manually invalidating `foo`
![Reactivity_Svelte_07](//images.contentful.com/zojzzdop0fzx/2bBtCuZmLQC5SbK43BSvMs/14ec7fd847b2cbc7f53057e6c8e7989b/reactivity7.gif)

* when mutating arrays, be mindful of adding an assignment at the end to let the compiler know that it must update the template references.

## Wrapping up
In general, whenever an assignment is made it will compile into an `$$invalidate` method that will mark the component as dirty and apply the required changes to the DOM.
 If there's any reactive variable (or statement) it will check if the component is marked as dirty and if any of its dependencies changed (because it has been invalidated), if that's the case then it will also invalidate it.
Store auto subscription creates an assignment that invalidates the `$` prepended variable when the store emits a new value.

*This Dot Labs is a modern web consultancy focused on helping companies realize their digital transformation efforts. For expert architectural guidance, training, or consulting in React, Angular, Vue, Web Components, GraphQL, Node, Bazel, or Polymer, visit [thisdotlabs.com](https://www.thisdotlabs.com).*

*This Dot Media is focused on creating an inclusive and educational web for all.  We keep you up to date with advancements in the modern web through events, podcasts, and free content. To learn, visit [thisdot.co](https://www.thisdot.co).*