Unlocking reactivity with Svelte and RxJS
Tags: svelte, rxjs, reactive, javascript
Published: 2020-02-18 15:21:54

---
title: Unlocking reactivity with Svelte and RxJS
description: I keep being surprised by how reactive Svelte feels. In this article, we take a glance at the Svelte internals to see how Svelte accomplishes this. This is important if we want to use RxJS in Svelte.
tags: svelte,rxjs,reactive,javascript
canonical_url: https://timdeschryver.dev/blog/unlocking-reactivity-with-svelte-and-rxjs
cover_image: https://timdeschryver.dev/blog/unlocking-reactivity-with-svelte-and-rxjs/images/banner.jpg
published: true
---

Follow me on Twitter at [@tim_deschryver](https://twitter.com/tim_deschryver) | Originally published on [timdeschryver.dev](https://timdeschryver.dev/blog/unlocking-reactivity-with-svelte-and-rxjs).

-------



As I keep playing around with Svelte, I keep being surprised how reactive it feels.
In this article, we'll take a quick glance at the Svelte internals to see how Svelte accomplishes this under the hood.

This is important to know, because we can use this knowledge to unlock the potentials of Svelte in combination with RxJS, without all the overhead, to end up with a truly reactive architecture. When we have a better understanding of the internals, we'll go through some examples to take a look at the possibilities.

### A Svelte component

To take a look at the internals we need a small demo application, and for this article, we have a simple counter that increments after each second.

```html
<script>
  let tick = 0
  setInterval(() => {
    tick += 1
  }, 1000)
</script>

{ tick }
```

To know how Svelte compiles the above code, let's have a look at it.
In the compiled code we see that Svelte wraps the increment assignment with an [`$$invalidate` method](https://github.com/sveltejs/svelte/blob/master/src/compiler/compile/render_dom/invalidate.ts).
This method tells the component that the value of `tick` has changed, and it will flag the component as "`dirty`".
Because of this, the component knows has to update.

```js
/* App.svelte generated by Svelte v3.18.2 */
import {
  SvelteComponent,
  detach,
  init,
  insert,
  noop,
  safe_not_equal,
  set_data,
  text,
} from 'svelte/internal'

function create_fragment(ctx) {
  let t

  return {
    c() {
      t = text(/*tick*/ ctx[0])
    },
    m(target, anchor) {
      insert(target, t, anchor)
    },
    p(ctx, [dirty]) {
      if (dirty & /*tick*/ 1) set_data(t, /*tick*/ ctx[0])
    },
    i: noop,
    o: noop,
    d(detaching) {
      if (detaching) detach(t)
    },
  }
}

function instance($$self, $$props, $$invalidate) {
  let tick = 0

  setInterval(() => {
    $$invalidate(0, (tick += 1))
  }, 1000)

  return [tick]
}

class App extends SvelteComponent {
  constructor(options) {
    super()
    init(this, options, instance, create_fragment, safe_not_equal, {})
  }
}

export default App
```

The rest of the component's code is mostly untouched. The code can be seen in the `instance` method.
There's also the `create_fragment` method which binds the variables to the view.

It's possible to mimmick this update behavior by creating a [reactive statement](https://svelte.dev/docs#3_$_marks_a_statement_as_reactive). A reactive statement will be executed when one of its dependant values has changed.
You can create one by simply adding a `$:` prefix to the statement.

```html
<script>
  let tick = 0
  setInterval(() => {
    tick += 1
  }, 1000)

  $: console.log(tick)
</script>

{ tick }
```

The compiled output of the instance wraps the `console.log` within the `update` lifecycle hook of the component.

```js
function instance($$self, $$props, $$invalidate) {
  let tick = 0

  setInterval(() => {
    $$invalidate(0, (tick += 1))
  }, 1000)

  $$self.$$.update = () => {
    if ($$self.$$.dirty & /*tick*/ 1) {
      $: console.log(tick)
    }
  }

  return [tick]
}
```

### A svelte store

Now that we know how a value gets updated, we can take it a step further by creating a [Svelte Store](https://svelte.dev/docs#svelte_store). A store holds state and is typically used to share data between multiple components.

What's interesting for us, is that a store is subscribable. The most important piece of the [contract of a store](https://svelte.dev/docs#Store_contract) is the `subscribe` method. With this method, the store can let all the consumers know that its value has changed. With this, we can set up a reactive push-based architecture for our applications.

In the implementation below, a [custom store](https://svelte.dev/docs#svelte_store) is created with the initial value of `0`.
Inside the store, there's an interval to increment the store's value after each second.
The store doesn't return a value, but it returns a callback method that will be invoked when the store's subscription is destroyed.
Inside this callback method, we can put teardown logic. In our example, we use the callback method to clear the interval timer.

```html
<script>
  import { writable } from 'svelte/store'

  let tick = writable(0, () => {
    let interval = setInterval(() => {
      tick.update(value => value + 1)
    }, 1000)

    return () => {
      clearInterval(interval)
    }
  })

  let tickValue = 0
  tick.subscribe(v => {
    tickValue = v
  })
</script>

{ tickValue }
```

To update the view, we create a new variable `tickValue` and we use the `subscribe` method on the store to increment `tickValue` when the store's value has changed.

If we take a look at compiled output now, we see that it hasn't changed.
Just like the first example, Svelte will just wrap the assignment of `tickValue` with the `$$invalidate` method.

```js
function instance($$self, $$props, $$invalidate) {
  let tick = writable(0, () => {
    let interval = setInterval(() => {
      tick.update(value => value + 1)
    }, 1000)

    return () => {
      clearInterval(interval)
    }
  })

  let tickValue = 0

  tick.subscribe(v => {
    $$invalidate(0, (tickValue = v))
  })

  return [tickValue]
}
```

Because Svelte is a compiler, it can make our lives easier.
By using the `$` again, and by prefixing the store variable in the HTML, we see that the store's value will be printed out after it has changed. This is magic! It means that we don't have to create a variable if we want to access the store's value.

```html
<script>
  import { writable } from 'svelte/store'

  let tick = writable(0, () => {
    let interval = setInterval(() => {
      tick.update(value => value + 1)
    }, 1000)

    return () => {
      clearInterval(interval)
    }
  })
</script>

{ $tick }
```

So far, we've seen nothing special with the compiled output of the component.
But if we take a look now, we can see new internal methods, and that the code of the component instance has been modified.

```js
/* App.svelte generated by Svelte v3.18.2 */
import {
  SvelteComponent,
  component_subscribe,
  detach,
  init,
  insert,
  noop,
  safe_not_equal,
  set_data,
  text,
} from 'svelte/internal'

import { writable } from 'svelte/store'

function create_fragment(ctx) {
  let t

  return {
    c() {
      t = text(/*$tick*/ ctx[0])
    },
    m(target, anchor) {
      insert(target, t, anchor)
    },
    p(ctx, [dirty]) {
      if (dirty & /*$tick*/ 1) set_data(t, /*$tick*/ ctx[0])
    },
    i: noop,
    o: noop,
    d(detaching) {
      if (detaching) detach(t)
    },
  }
}

function instance($$self, $$props, $$invalidate) {
  let $tick

  let tick = writable(0, () => {
    let interval = setInterval(() => {
      tick.update(value => value + 1)
    }, 1000)

    return () => {
      clearInterval(interval)
    }
  })

  component_subscribe($$self, tick, value => $$invalidate(0, ($tick = value)))
  return [$tick, tick]
}

class App extends SvelteComponent {
  constructor(options) {
    super()
    init(this, options, instance, create_fragment, safe_not_equal, {})
  }
}

export default App
```

In the compiled output, we see the new `component_subscribe` method.
To know what it does, we can take a look at the [source code](https://github.com/sveltejs/svelte/blob/master/src/runtime/internal/utils.ts).

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

export function subscribe(store, ...callbacks) {
  if (store == null) {
    return noop
  }
  const unsub = store.subscribe(...callbacks)
  return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub
}
```

By looking at the code, we see that `component_subscribe` uses the `subscribe` method on the passed store instance to be notified when the store value is changed and when this happens it will invoke a callback.
In our compiled output, we notice that the callback method is `value => $$invalidate(0, $tick = value)`.

We can see here, that the callback receives the new tick value and that it updates the `$tick` variable with its new value. In the callback, we see `$$invalidate` again. This, to tell the component that the tick value has been changed and that it has been updated.

The last line in the `subscribe` method returns an `unsubscribe` method.
The method will be added to the component instance via `component.$$.on_destroy.push(subscribe(store, callback))`.
When the component gets destroyed, it will invoke all the added callback methods.
This is visible in the `create_fragment` method:

```js
function create_fragment(ctx) {
  let t

  return {
    c() {
      t = text(/*$tock*/ ctx[0])
    },
    m(target, anchor) {
      insert(target, t, anchor)
    },
    p(ctx, [dirty]) {
      if (dirty & /*$tock*/ 1) set_data(t, /*$tock*/ ctx[0])
    },
    i: noop,
    o: noop,
    d(detaching) {
      if (detaching) detach(t)
    },
  }
}
```

The `unsubscribe` method provides a place where we can put teardown logic.
This is important for our timer store because otherwise, the interval will continue to keep ticking.

If we don't prefix the store object in the HTML with the `$` sign, the compiled output looks as follows.
We can see that `tick` is now just an object, and that it isn't subscribed to.

```js
/* App.svelte generated by Svelte v3.18.2 */
function instance($$self) {
  let createTick = () => {
    let tickStore = writable(0, () => {
      let interval = setInterval(() => {
        tickStore.update(value => value + 1)
      }, 1000)

      return () => {
        clearInterval(interval)
      }
    })

    return tickStore
  }

  let tick = createTick()
  return [tick]
}
```

By looking at the compiled code and after a quick look at the source code, we can see that Svelte handled the store's subscription for us. Even more, it will also communicate with the component that its value is changed.

This code can be repetitive to write, and it can contain bugs when we forget to unsubscribe from the store. I'm happy that Svelte handles all of this for us, we only have to prefix the subscribable with the `$` sign, and Svelte will do all the rest.

### Svelte with RxJS

We've seen a bit on how Svelte accomplishes reactivity with a Svelte Store.
But with what we've seen so far, we can see that it resembles the contract of an [RxJS Observable](https://rxjs-dev.firebaseapp.com/guide/observable).

> Take a look at the [TC39 proposal](https://github.com/tc39/proposal-observable) to introduce Observables to ECMAScript.
> This proposal, offers a similar contract to the implementation of both RxJS and Svelte.

Because an Observable also has a `subscribe` method, which also returns a callback method to unsubscribe, we can replace the store implementation with any RxJS Observable.

For the tick example, we can use a [RxJS timer](https://rxjs-dev.firebaseapp.com/api/index/function/timer).
The timer is similar to the `setInterval` method, as it will emit an incremented number after each second.
This just magically works, and we've written a whole less code!

```html
<script>
  import { timer } from 'rxjs'
  let tick = timer(0, 1000)
</script>

{ $tick }
```

When we take a look at the compiled code for the RxJS implementation, we see nothing has changed.
We still see the `component_subscribe` method together with the callback to increment the tick value, and we also see that the subscription will be unsubscribed to.

```js
/* App.svelte generated by Svelte v3.18.2 */
import {
  SvelteComponent,
  component_subscribe,
  detach,
  init,
  insert,
  noop,
  safe_not_equal,
  set_data,
  text,
} from 'svelte/internal'

import { timer } from 'rxjs'

function create_fragment(ctx) {
  let t

  return {
    c() {
      t = text(/*$tick*/ ctx[0])
    },
    m(target, anchor) {
      insert(target, t, anchor)
    },
    p(ctx, [dirty]) {
      if (dirty & /*$tick*/ 1) set_data(t, /*$tick*/ ctx[0])
    },
    i: noop,
    o: noop,
    d(detaching) {
      if (detaching) detach(t)
    },
  }
}

function instance($$self, $$props, $$invalidate) {
  let $tick
  let tick = timer(0, 1000)
  component_subscribe($$self, tick, value => $$invalidate(0, ($tick = value)))
  return [$tick, tick]
}

class App extends SvelteComponent {
  constructor(options) {
    super()
    init(this, options, instance, create_fragment, safe_not_equal, {})
  }
}

export default App
```

With this example, we see that a Svelte Store can be substituted with an RxJS observable.
As someone who's using Angular with NgRx daily, this is something I can use to my advantage.
Because once you get to know RxJS, it makes it easier to work with asynchronous code and it hides all the (complex) implementation details.

### RxJS-based examples

#### Typehead

It's been a while since I had to write a typeahead without RxJS but this took some time and a lot of code. The implementation also contained fewer features, as the cancellability of previous requests. Sadly, most of the time, the implementation also introduced bugs.

But with RxJS, this becomes trivial.
By using some [RxJS operators](https://rxjs-dev.firebaseapp.com/guide/operators) we end up with a working typeahead, without the bugs, which is thoroughly tested, and has more features. All of this, with less code.

The implementation with RxJS looks as follows:

```html
<script>
  import { of, fromEvent } from 'rxjs'
  import { fromFetch } from 'rxjs/fetch'
  import {
    map,
    concatMap,
    catchError,
    switchMap,
    startWith,
    debounceTime,
  } from 'rxjs/operators'
  import { onMount$ } from 'svelte-rx'

  let inputElement

  const books = onMount$.pipe(
    concatMap(() =>
      fromEvent(inputElement, 'input').pipe(
        debounceTime(350),
        map(e => e.target.value),
        switchMap(query => {
          if (!query) {
            return of([])
          }
          return fromFetch(
            `https://www.episodate.com/api/search?q=${query}`,
          ).pipe(
            switchMap(response => {
              if (response.ok) {
                return response.json()
              } else {
                return of({ error: true, message: `Error ${response.status}` })
              }
            }),
            catchError(err => of({ error: true, message: err.message })),
          )
        }),
        startWith([]),
      ),
    ),
  )
</script>

<input bind:this="{inputElement}" />

<pre>{ JSON.stringify($books, ["tv_shows", "id", "name"], 2) }</pre>
```

The code above creates a reference to the input box by using Svelte's [`bind:this`](https://svelte.dev/docs#bind_element) attribute.
When the component is mounted, we use RxJS to subscribe to the `input` event on the input box. The rest of the code fires an AJAX request to an API and binds the result to the `books` variable.
In the HTML, we print out the output by subscribing to the `books` variable with the `$` sign.

#### Refactored Typehead

The above code can be cleaned up. What I don't like about it, is the usage of the `inputElement` binding.
Because, again, this adds extra code in our codebase that we have to maintain.

Instead, we can use an [RxJS Subject](https://rxjs-dev.firebaseapp.com/guide/subject).
The only problem is that the contract is a little bit different.
Svelte uses the `set` method to set a new value, while RxJS uses the `next` method.
The rest of the contract is complementary.

This is solvable by assigning the `set` method to the `next` method.

```js
const subject = new BehaviorSubject('')
subject.set = subject.next
```

Or a better approach is to introduce a new `SvelteSubject`, as mentioned in a [GitHub issue](https://github.com/ReactiveX/rxjs/issues/4740#issuecomment-490601347).

```js
class SvelteSubject extends BehaviorSubject {
  set(value) {
    super.next(value)
  }

  lift(operator) {
    const result = new SvelteSubject()
    result.operator = operator
    result.source = this
    return result
  }
}
```

The implementation now looks as follows, notice that the [`bind:value` attribute](https://svelte.dev/docs#bind_element_property) is used to bind the Subject to the input box. To fire the AJAX requests, we subscribe directly to the Subject and we don't have to wait until the component is mounted.

```html
<script>
  import { of, BehaviorSubject } from 'rxjs'
  import { fromFetch } from 'rxjs/fetch'
  import {
    map,
    concatMap,
    catchError,
    switchMap,
    startWith,
    debounceTime,
  } from 'rxjs/operators'

  const typeAhead = new BehaviorSubject('')
  typeAhead.set = typeAhead.next

  const books = typeAhead.pipe(
    debounceTime(350),
    switchMap(query => {
      if (!query) {
        return of([])
      }
      return fromFetch(`https://www.episodate.com/api/search?q=${query}`).pipe(
        switchMap(response => {
          if (response.ok) {
            return response.json()
          } else {
            return of({ error: true, message: `Error ${response.status}` })
          }
        }),
        catchError(err => of({ error: true, message: err.message })),
      )
    }),
    startWith([]),
  )
</script>

<input bind:value="{$typeAhead}" />

<pre>{ JSON.stringify($books, ["tv_shows", "id", "name"], 2) }</pre>
```

#### React to changes

The benefit of reactive programming is that we can react to changes.
To illustrate this, the example below creates multiple Observable streams based on a Subject to transform the Subject's value.

It's also possible to set a new value for the Subject programmatically, this will also update the input's value.

```html
<script>
  import { of, BehaviorSubject } from 'rxjs'
  import { map, delay } from 'rxjs/operators'

  export const name = new BehaviorSubject('')
  name.set = name.next

  const nameUpperCase = name.pipe(map(n => n.toUpperCase()))
  const nameDelayed = name.pipe(delay(1000))
  const nameScrambled = name.pipe(
    map(n =>
      n
        .split('')
        .sort(() => 0.5 - Math.random())
        .join(''),
    ),
  )

  function clear() {
    name.set('')
  }
</script>

<input bind:value="{$name}" />
<button on:click="{clear}">
  Clear
</button>

<p>Hello, {$name}</p>
<p>Uppercased: {$nameUpperCase}</p>
<p>Delayed: {$nameDelayed}</p>
<p>Scrambled: {$nameScrambled}</p>
```

### Conclusion

In this article, we saw that an RxJS Observable can act as a drop-in replacement to a Svelte store.
This is probably a coincidence, but this makes it very pleasant to work with.
For me, this makes Svelte the most reactive "framework" at the moment and is a glance into the future.

We already see that RxJS is heavily used in the Angular and React communities, even in the internals of Angular.
For the most part, we have to manage the subscriptions ourselves. At the start this is hard to get right, and bad practices will sneak into the codebase. For example, Angular has the `async` pipe to handle manage the subscription. But some codebases don't use the pipe and are using the `subscribe` method instead, without unsubscribing from the Observable.
Svelte makes the pit of success larger because it hides all of this from us at compile time. I would love to see this first-class Observable support in Angular.

Svelte and RxJS are known for the little amount of code we have to write, that's one of the reasons what I like about them.
In the past, I tried to create some proof of concepts with svelte, but I usually ended up missing some of the features that RxJS provides.
Now that I know that they complement each other well, I will grab this combination more often.


-------

Follow me on Twitter at [@tim_deschryver](https://twitter.com/tim_deschryver) | Originally published on [timdeschryver.dev](https://timdeschryver.dev/blog/unlocking-reactivity-with-svelte-and-rxjs).