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.
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.
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
This method tells the component that the value of
tick has changed, and it will flag the component as "
Because of this, the component knows has to update.
The rest of the component's code is mostly untouched. The code can be seen in the
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. 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.
The compiled output of the instance wraps the
console.log within the
update lifecycle hook of the component.
Now that we know how a value gets updated, we can take it a step further by creating a 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 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 is created with the initial value of
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.
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
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.
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.
In the compiled output, we see the new
To know what it does, we can take a look at the source code.
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
The method will be added to the component instance via
When the component gets destroyed, it will invoke all the added callback methods.
This is visible in 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.
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.
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.
Take a look at the TC39 proposal 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.
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!
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.
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.
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 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:
The code above creates a reference to the input box by using Svelte's
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
In the HTML, we print out the output by subscribing to the
books variable with the
The above code can be cleaned up. What I don't like about it, is the usage of the
Because, again, this adds extra code in our codebase that we have to maintain.
Instead, we can use an RxJS 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
The rest of the contract is complementary.
This is solvable by assigning the
set method to the
Or a better approach is to introduce a new
SvelteSubject, as mentioned in a GitHub issue.
The implementation now looks as follows, notice that the
bind:value attribute 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.
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.
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.
Please consider supporting me if have you enjoyed this post and found it useful: