Truly Reactive Programming with Svelte 3.0
Learn how Svelte moves reactivity from an API into the JavaScript language itself.
That title is a bit dramatic, but then again, so is Svelte and the idea behind it. If you don't know what Svelte is yet, then strap in — you're about to witness a revolution, and it's going to be quite the ride (no pressure on the Svelte team 😅).
Note that this is not a tutorial on how to get started with Svelte. There is already a great step-by-step interactive tutorial by the Svelte team that eases you into the world of reactive programming.
Disclaimers are in order: If I say something that sounds stupid in this article, please let me know.
Alright, let's get into it!
Before I get into why I think Svelte is so disruptive, let's take a look at this tweet from the man, Dan, from a while back and dissect what it actually means:
https://twitter.com/dan_abramov/status/1025801430668664833
Yet another disclaimer: This article is not meant to bash React in any way. I simply decided to use React as a case study because most people who read this article will have used it at one point or another. It’s just the best example to contrast Svelte against.
What did Dan mean, and what effect does this have on the way we currently write code? To answer this question, let me give you a simplififed view of how React works behind the scenes.
When you render a React app, React keeps a representation of the DOM in something called the Virtual DOM. The Virtual DOM acts as a middleman of sorts between your React code and what your browser paints on the screen.
Then, when your data changes (maybe you called this.setState
), React does a bit of work to determine how to repaint your UI on the screen.
It compares the Virtual DOM against the real DOM to determine what changed due to this data update. It then repaints only the parts of the DOM that do not match the new copy in the Virtual DOM, eliminating the need to repaint the whole DOM every time there's a data update.
While the Virtual DOM is very fast compared to other alternatives, it's still an extra layer of complexity that the browser has to deal with along with the actual DOM updates.
Another thing you might have noticed is that if you don't notify React that your data has changed (i.e, by calling this.setState
), your Virtual DOM will not change, and React will not react (ba dum tss! 🤓).
This is what Dan meant when he said React isn't fully reactive. React is relying on you to track your app's data and to tell it when there are updates. This is more work for you to do and yes, there's a better way.
Svelte is a whole new way of building UI in a blazingly fast, efficient, and truly reactive manner, all without using a virtual DOM in fewer lines of code that you'd write with any other framework/library.
That sounds all nice and good, but how is it different from the myriad of other JavaScript frameworks/libraries out there, you ask? I'll tell you.
1. True Reactivity
Svelte is not a library. Svelte is not a framework. Rather, Svelte is a compiler that takes in your code and spits out native JavaScript that interacts with your DOM directly with no need for an intermediary.
Wait, what? A compiler? Yes — a compiler. It’s such a good idea that I don’t know why it wasn’t so obvious until now, and I’ll tell you why I think it’s so cool.
Here's a quote from Rich Harris' talk at the YGLF 2019 conference:
Svelte 3.0 moves reactivity out of the component API and into the language.
What does that mean? Well, we've seen how React (and most other frontend frameworks) requires you to use an API to tell it that data changed (this.setState
) before it knows to rerender.
This need to call this.setState
means that the reactivity of your app is tied to a specific API, without which, it would be completely unaware of data changes.
Svelte takes another approach to solving this problem.
It has taken inspiration from Observable in the way it runs your code. Instead of running your code top to bottom, it runs it in topological order.
Look at the snippet of code below, and we'll go through what it means to run code in topological order.
If this code was parsed top to bottom, const secondNumber = square(firstNumber)
would throw an error because at this point, firstNumber
has not been defined yet.
However, running the code in topological order would not result in any errors. How is this? Well, the compiler wouldn't run this code from top to bottom. Rather, it would take a look at all the variables first and generate a dependency graph (i.e who needs who first).
In this case, here's an extremely simplified look at how a compiler would compile this code topologically.
At first glance, it seems that the code is being parsed top to bottom, but take a closer look and you'll discover that it actually does some jumping around.
When it gets to line 4, the compiler discovers that it doesn't have firstNumber
so it pauses the execution there and looks ahead in your code to see if you defined it anywhere. Well, we did exactly that on line 5, so it runs line 5 first before going back to line 4 to execute it.
TL;DR If statement A depends on statement B, then statement B will run first regardless of the order of declaration.
So how does this apply to the way Svelte implements reactivity?
You can label a statement with an identifier in JavaScript, and that operation looks like this:
$: foo = bar
.
All that does is add an identifier called $
to the foo = bar
statement (note that this would fail in strict mode if foo
wasn't defined earlier).
So in Svelte's case, whenever it sees a statement prefixed with $:
, it knows that the variable on the left derives it value from the variable on the right. We now have a way of binding one variable's value to another's.
This means that we're now using a core part of JavaScript's API to achieve true reactivity instead of relying on third-party APIs like this.setState
.
This is how it looks like in practice:
Notice how in the code above, we didn't need to reassign bar to the new value of foo
— either by doing it directly like bar = foo + 10
or by calling an API like this.setState({ bar: foo + 10 })
. It is handled automatically for us.
This means that when you change bar
to equal 15, foo
is updated to be 25 automatically, and you don't need to call an API to update it for you. Svelte already has your back.
The compiled version of the Svelte code above looks like this:
Take your time to really study this piece of code above. Really take your time.
Do you see how the update on foo
happened before bar
was even defined? That’s because the compiler is parsing the Svelte code in topological order instead of a strictly top-down order.
Svelte is reacting on its own to data changes. It doesn’t want you to worry about tracking what changed and when; it knows automatically.
Note: On line 4, bar
's value is not updated until after the next Event Loop so that performance is not negatively affected.
This allows you to stop worrying about manually updating your state whenever your data changes. You can focus on your logic all day while Svelte helps you to reconcile your UI with your latest state.
2. Brevity
Remember how I said Svelte allows you to do so much with fewer lines of code written? I meant it. I’ll show you a simple component in React and its equivalent in Svelte, and you judge for yourself:
These two apps are completely identical in functionality but you can see how much more code we had to write in React — and don’t even get me started on Angular 😂.
Apart from the Svelte code being more pleasing to the eye, it is also much easier to reason about as there are less moving parts than in the React code. We didn’t need an event handler to update the value of the input element — simply binding the value was enough.
Imagine you were just starting out learning web development. Which code would have confused you more? The one on the left, or the one on the right?
While this might seem like a trivial point to make, it quickly becomes clear how useful it is to write fewer lines of code when you start to build bigger and more complex apps. I’ve personally found myself spending hours trying to understand how a large React component my teammate wrote works.
I honestly believe that Svelte’s simplified API will allow us to read and understand code much faster, improving our overall productivity.
3. Performance
So we've seen how reactive Svelte is and how it allows you to do more with less. What about performance? What is the user experience like with apps written entirely in Svelte?
One of the reasons React is so powerful is because of how it uses the virtual DOM to minimize how much it updates the real DOM.
A downside to this approach, though, is that if a component's data changes, React will re-render that component and all its children whether they need to re-render or not. This is why React provides APIs like shouldComponentUpdate
, useMemo
etc.
This problem is not specific to React. Any framework/library that utilises some sort of a virtual DOM will suffer from this issue.
Svelte, on the other hand, doesn't use a virtual DOM. So how does it tackle the issue of matching your app's UI to it's state? Let me quote Rich Harris again from his wonderful YGLF talk:
Frameworks are not tools for organising your code. They are tools for organising your mind.
The quote above is what led Rich to the idea that a framework could be something that runs in a build step, eliminating the need for your code to have an intermediary at runtime. This idea is why Svelte is a compiler and not a framework.
That simple idea is why Svelte is really fast. Svelte compiles your code down to an efficient, low-level code that interacts with the DOM directly. This is all well and good, but how does Svelte solve the issue of repainting the whole DOM when data changes?
The difference lies in the way a framework like React knows what changed versus how Svelte does the same thing. We’ve seen how React relies on you to call an API method to tell it when your data changes, but with Svelte, simply using the assignment operator =
is enough for it.
If a state variable — let’s say foo
— is updated using the =
operator, Svelte will only update the other variables that depend on foo, like we saw earlier. This allows Svelte to only repaint the parts of the DOM that derive their value in one way or another from foo
.
I’m going to leave out the actual implementation of how this works because this article is already long enough. You can watch Rich Harris himself explain that.
Svelte 3.0 is one of the best things to happen to software development in a while. Some may say that that is an exaggeration, but I disagree. The concept behind Svelte and its execution will enable us to do more while shipping less boilerplate JS to the browser.
This, in turn, will allow for apps that are more performant, more lightweight, and produce code that is easier to read. Now, will Svelte be replacing React, Angular, or any of the other established frontend frameworks anytime soon?
For now, I can say the answer is no. Svelte is relatively new compared to those, so it needs time to grow, mature, and sort out some kinks we might not even know exist yet.
Just like React changed software development when it came out, Svelte, too, has the potential to change how we think about frameworks and what is possible when we create new boxes to think in.
Happy coding!