Why you should give Svelte a try
“Frameworks are not tools for organizing your code, they are tools for organizing your mind”
- Rich Harris
On April 4th 2019, Rich Harris (@rich_harris) presented Svelte version 3 to the world at YGLF Code Camp conference (See the talk here). We discovered there is an alternative way to track state changes in a UI framework, moving reactivity into the language itself.
What is Svelte?
Svelte is the new big thing! It’s a breath of fresh air in the UI frameworks ecosystem. Faster, Lighter and Cooler*.
* Cooler? Just my personal opinion.
But let’s start from the beginning.
Svelte is a UI framework created by Rich Harris, graphics editor at The New York Times. He had an idea back in 2016 about a different way to manage state in a UI framework. He had already created a bundler called Rollup.js and an ES6 transpiler called Bublé, so he was in a very good position to give it a try and start a new framework from scratch.
A new framework with a different approach, trying to move the reactivity system from the user’s and library's hands into the language. To make this more relatable, I'm going to compare React Vue and Svelte reactivity systems.
Let's start with react: React is not fully reactive.
You probably know that already. And if you don't, please don't blame me, these are not my words, they have been extracted from a Dan Abramov tweet in 2018.
The idea behind these words is this: every component has its own state. And to make it reactive, the only way to change or modify that state is by using a method or function provided by react (setState method if you are using classes or the function provided by the useState hook if you are using hooks).
<p> CODE: https://gist.github.com/gomezcabo/090a1efc3dc0783073e37678e9559e70.js?file=state-in-react.js</p>
We are basically saying this:
Hey React, here's the new state, please react, please update whatever you need to update, apply the changes to your virtual DOM, compare this new version of virtual DOM with the previous snapshot, and finally update the real DOM if you find any difference.
That's a lot of work to do, right?
What about Vue?
We don't need to use a method or function provided by Vue to update the state, we just mutate the attributes defined in the state. This feels more reactive, but behind the scenes, a similar process is happening.
<p> CODE: https://gist.github.com/gomezcabo/090a1efc3dc0783073e37678e9559e70.js?file=state-in-vue.js</p>
In Vue, we define the component state by defining a data function which returns the initial state. The way Vue makes it reactive is by replacing every attribute in the state (at any level) with a getter and a setter. That way, when we change the state doing this.count += 1, we are not updating the state directly, we are calling a setter generated and controlled by Vue. So Vue always knows when the state is changing. Once it detects a change, the process is similar to React, compute the dependencies, update the virtual DOM, compare with the previous version, and finally, if it finds any difference, update the real DOM. We are skipping one step (we don't need to tell Vue we are changing the state), but the rest of the process is somewhat similar.
Finally, how does Svelte reactivity work?
As I mentioned before, the idea behind Svelte is to move reactivity into the language. We define a variable and automatically it becomes reactive. We can update that variable later and then everything that depends on that variable will change.
The big difference between Svelte and the other frameworks is that Svelte works in the build time because it's a compiler. Other frameworks work in the runtime, so even if we write a very small piece of code (like a hello world app) we will need to ship our little code, with the whole framework, the whole Vue.js library or the whole React and ReactDOM libraries. That’s why Svelte is a compiler, not a dependency.
Since Svelte is a compiler, it can take out everything around our code, keep it as it is, and put some reactivity instruments inside. That way, every time an assignment happens, it will explicitly compute the dependencies and update the real DOM. Svelte can do that because, again, it is a compiler and can compute the dependencies in the build time.
How does it look? Very simple: after every assignment Svelte will add this little instruction.
<p> CODE: https://gist.github.com/gomezcabo/090a1efc3dc0783073e37678e9559e70.js?file=state-in-svelte.js</p>
When this $$invalidate function is called, it will mark the count variable as changed, and it will check the dependencies and will update the real DOM wherever is needed. We can see this by using the REPL environment provided on the Svelte website (https://svelte.dev/repl).
Let’s write a very simple component that shows two counters (count1 and count2), the result of an operation count1 + count2, and a button to update those values.
<p> CODE: https://gist.github.com/gomezcabo/090a1efc3dc0783073e37678e9559e70.js?file=example1.svelte</p>
The JS output of the compiled component would be this:
<p> CODE: https://gist.github.com/gomezcabo/090a1efc3dc0783073e37678e9559e70.js?file=svelte-compiled-component.js</p>
A Svelte component is defined as a class with a constructor that calls an init function with some options. The most interesting are the instance and create_fragment functions.
The instance function
<p> CODE: https://gist.github.com/gomezcabo/090a1efc3dc0783073e37678e9559e70.js?file=instance-function.js</p>
The create_fragment function
The create_fragment function creates a closure with references to all DOM elements rendered by the component and export several functions for different responsibilities:
- Create the real DOM elements (the create function).
- Build the tree structure with the previously created elements and mount the top level elements into the DOM element target, the place where we want to mount our component (the mount function).
- Check for updates in all defined variables that are being used in the template and if something has changed, update the DOM (the update function).
- Detach all child components and dispose of the components (the destroy function).
<p> CODE: https://gist.github.com/gomezcabo/090a1efc3dc0783073e37678e9559e70.js?file=create-fragment-function.js</p>
When the button is clicked, both count variables will change and be marked as changed. Then, when the update function is called in the next cycle, it will detect the changes and will update the specific DOM nodes that need to be updated. And all of this without using a virtual DOM, that’s the magic!
Benefits of using Svelte
This new way of writing components and handling the state in a UI framework have some benefits.
As an example, the same component written in React with hooks (23 lines vs 17 lines):
<p> CODE: https://gist.github.com/gomezcabo/090a1efc3dc0783073e37678e9559e70.js?file=example1.jsx</p>
Areas to improve
By the time I’m writing this article there are some areas where Svelte has a lot of space to improve. Most of them because version 3 is very young yet (released a few weeks ago).
Svelte doesn’t come with a fancy CLI with hot-reloading, template project creation based on user selected options, and all of these features that we, as front-end developers, love. Also, editors and IDEs support could improve and there are not many plugins written yet.
Svelte 3 was written in Typescript and with Typescript support in mind (See this conversation on Github), but as for today, there is no official Typescript support. It will probably come in the next few months.
Don’t get me wrong, the community is amazing, and is always up to help with any issue you have (https://svelte.dev/chat). However, despite it’s fast growth in the last few weeks, it’s still small compared to the other mainstream frameworks. That means less resources (courses, books, tutorials) and less open source libraries and components. If you need, let’s say, a color picker, you will probably find more options written in React or Vue.
Given all the benefits of using Svelte (leaner components, less code, less abstractions, smaller bundles and an impressively good performance), would you give it a try? I highly recommend you to go through the interactive tutorial on the Svelte website and then use the Svelte REPL environment (https://svelte.dev/repl) to start playing around. I promise you will have fun!