As we all know, there is a new Javascript framework coming out every day. Thankfully, there are 3 big ones with stable communities and a lot of additional libraries that fulfill them: React, Angular and Vue. But not so long ago, Vue was also a new kid on the block that got pretty popular very fast. So who says that we should disregard all the new ones so easily? Today we’ll be looking at Svelte, a somewhat new framework made by Rich Harris. This article will cover the general intro to Svelte while making comparisons with React.
My name is Josip and I’m a full-stack developer at CROZ, and I mostly use React to write large applications. From time to time I mentor and teach young students and developers on how to properly use React for their projects.
Intro to Svelte
Svelte is a relatively new framework for building user interfaces. It’s latest major version v3 was released April 21, 2019.
The three major selling points that their page markets are:
– shifting most of the work to compile step
– writing less boilerplate
– not using Virtual DOM
As you can see, Svelte uses a compiler to build, as they say it, *a highly efficient imperative code that surgically updates the DOM*. To do this, Svelte uses special file type .svelte
, which consists of script, style and template parts. They also add some new syntax to script and template parts to achieve the reactivity of their components.
All this results in less actual code, as a lot of features are moved to the language level. At last, Svelte author Rich Harris raised holy war on Virtual DOM by claiming that it is pure overhead and that reason behind Svelte’s speed is updating the DOM directly. But enough of the intro, let’s look how do we even code in Svelte.
Create a Svelte app
As a React dev, I immediately started looking if there is something like create-react-app
. The recommendation is that I should use npx degit sveltejs/template my-svelte-project
. WTH is degit
now? By goggling a bit I find out that degit
is another tool by Rich Harris, and it works like git clone --depth 1
, but with side effects that make it more friendly for project bootstrapping. Cool.
After running that script I get a project with this structure:
/public favicon.png global.css index.html /src App.svelte main.js .gitignore package.json package-lock.json README.md rollup.config.js
It’s a relatively small project with an only rollup and svelte dependencies, without any additional tools.
After running npm install
and npm run dev
I got to access my first Svelte app on http://localhost:5000
.
Our main component looks like this:
<script> export let name; </script> <main> <h1>Hello {name}!</h1> <p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p> </main> <style> main { text-align: center; padding: 1em; max-width: 240px; margin: 0 auto; } h1 { ... } </style>
As previously said, there are 3 parts:
- script, where component logic and state reside
- style, where the CSS is
- template, for component markdown I try to change some parameters and it works like a charm, all with live reload.
Also, writing new simple components is quite easy and very similar to React. You can actually do it without looking at any tutorial because the syntax is simple and pretty much the same.
Props, local state, conditions, and loops
Props
Sending and receiving props is Svelte is quite easy. You declare a prop by exporting a variable from the component:
<script> export let text; </script> <li>{text}</li>
And to send data from the parent component, you do it just like in React:
<Todo text="Buy lunch!"/>
This can be used both for sending data or functions to child components.
Although you can send functions as props in Svelte, there is another way to handle callbacks from child components. It uses a concept of events, and gives child component a means to dispatch an event to its parent component:
<script> import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); export let text; function onClick() { dispatch('message', { text: "Clicked!" }); } </script> <li on_click={onClick}>{text}</li>
Where parent component can handle just like it would for DOM events:
<Todo text={todo} on_message={e => console.log(e.detail.text)}/>
If you have any experience with Angular, this may remind you of the @Output
directive.
State
If you use hooks in React, the state is defined using useState hook which returns a value and set handler. Svelte uses its compiler capabilities to implement this a little easier to read. To use local component state in Svelte all you need is to declare a variable in the script block, and you can manipulate it just as you would in vanilla JS.
The Svelte compiler adds the necessary logic to track changes on your variable and updates the HTML accordingly. Even though this seems easier to read, it becomes a bit like black magic in comparison to React’s useState.
<script> let counter = 0; const onClick = () => counter += 1; const reset = () => counter = 0; </script> <button on_click={onClick}>Increase</button> <button on_click={reset}>Reset</button> <p>Count: {counter}</p>
Here is where it gets weird.
You know those useMemo
and useCallback
hooks? Well, Svelte has something like this. If you declare a variable like this: $: double = counter * 2;
, Svelte will recalculate it only if counter
changes. As you can see, it uses Javascript label syntax to tell the compiler where recalculation should be carefully managed. And you thought that no one uses labels any more.
The problem I have with this approach to state and prop variables is that the difference between them is very blurry. In React you can clearly see that a prop is input to component (because of clear function notation), and that state is something internal. In Svelte they are both just variables, with the exception that props use export
keyword. Common problems with this could be accidental omitting of export
or unintentional changing of prop value.
Conditions and loops
The main thing I don’t like with frameworks that use templates is how they handle conditions and loops. You always need to use some new type of syntax to get what you want, and it seems unnatural for Javascript/HTML world. If you’re like me, you probably won’t like how Svelte does it. To add conditions in your Svelte templates, you need to use {#if } {:else} {/if}
notation. Generally, you use #
for an opening block, /
for closing, and :
for continuation block (as in {:else}
).
Example taken from Svelte tutorial:
<script> let user = { loggedIn: false }; function toggle() { user.loggedIn = !user.loggedIn; } </script> {#if user.loggedIn} <button on_click={toggle}> Log out </button> {:else} <button on_click={toggle}> Log in </button> {/if}
I feel that React handles this a bit better:
function Component() { let [loggedIn, setLoggedIn] = React.useState(false); function toggle() { setLoggedIn(!loggedIn); } if (loggedIn) { return ( <button onClick={toggle}> Log out </button> ); } else { return ( <button onClick={toggle}> Log in </button> ); } }
Using loops is very similar, and it’s done with {#each}
block:
<script> import Todo from './Todo.svelte'; export let todos = []; </script> {#each todos as todo} <Todo text={todo.text}/> {/each}
If we go back to React, we remember that there we need to provide key prop which actually helps performance. Svelte supports this with additional parameter in brackets at the end of {#each}
block:
{#each todos as todo (todo.id)} <Todo text={todo.text}/> {/each}
Verdict
There are more Svelte topics we could cover, but let’s stop here and summarize our opinion. Svelte seems quite interesting at first glance. It feels very similar to React, but with less code we need to write to get things going. In my opinion, the way Svelte tackles boilerplate reduction results in code that is readable, but users will need some time to adjust to it, some could think that it’s weird, and it could be confusing to new developers that come from vanilla JS. Also, using templates and that additional curly brace syntax seems ugly after years of elegant JSX.
The performance problem Svelte tries to fix is also questionable. You may ask yourself, in how many cases have you had any performance issue with React where the source of the problem was Virtual DOM? You can also ask, how performant is Svelte model in large, UI heavy applications, and are there any drawbacks?
The thing that I did not mention before is that you cannot fully use Typescript with Svelte at the time, which could be decisive for some of you. But don’t let that turn you away, as Rich is actively trying to add support for it in newer versions.
I, personally, am still skeptical about using Svelte in my projects. It still needs time to get a community around it, with more examples of real-world applications.
I may try it on some small hobby apps but I’ll stick to React for creating large applications for clients. But I’ll surely keep an eye on it! What is your opinion?
Originally published on JavaScript January.