Blogg

Tankar från vår verklighet.

Gustav Jorlöv

Svelte - the framework for those who don't want a framework

Gustav Jorlöv

The hype around web frontend frameworks seems like a never ending story. This article is not trying to address that problem. But sometimes there comes around something that challenge the norm of how things are done. React was one of those frameworks that completely revolutionized the industry thanks to some controversial ways of structure code.

Svelte uses new controversial ways of going about things that have caught my interest. In this article I will present an overview of Svelte.

Problems to solve

Bundle size

A common problem with web applications today is that the bundle size is way too large to provide a fast user experience. Not only does the code need to be delivered over a network, it also needs to be parsed before anything can be painted to the screen. A common problem with almost all frameworks today. Solutions like code splitting and server side rendering exists but I rarely see that they are used. Code splitting can also not be done on the framework bundle, typically the largest portion of the bundle anyway.

Performance

Declarative code style is very popular. In the case of React, given a set of input props it declares what markup should be produced. Not how, but what markup. Under the hood React uses a “virtual dom” mechanism that compares every single piece of the old markup against a proposed new markup. If any difference is found, React will access the “browser dom” and perform the actual update.

Things to learn

Developers like things that are easy to learn but have a flexible nature. A trend amongst frameworks since the jQuery days is that there are typically less framework specific details to learn. jQuery had a ton of API to learn, Angular 1.x had its share of boilerplate, React has reduced that even more. But this article is about to describe Svelte, which in my humble opinion has been the easiest framework to learn to date since there actually aren’t that much to it.

Solution attempts

The creator of Svelte, Rich Harris, recognized that frameworks aren’t made for organizing code, they only organize our minds. Meaning that the translation from declarative code (the what) to imperative code (the how) might as well be done at build time rather than at run time. This moves the necessity of a framework being present from the client browser to the build step. If we don’t need to ship a framework to the client, the bundle size can be significally smaller.

Svelte also doesn’t use a “virtual dom” to keep track of updates, it uses shallow equality comparison along with a “reactive operator” to know what to update. It does so by producing the actual dom api calls needed to make the update. That also gets rid of a complete category of performance problems. Therefore Svelte is also very fast.

Svelte is currently at version 3, which fundamentally works the same way as version 2, but it focuses a lot on the developer experience. That’s what caught my interest and has kept me going, since there actually aren’t that much API details to remember.

Developing in Svelte

Start to play around with Svelte using the following commands, which effectively clones an example project setup:

  npx degit sveltejs/template new_app

The project setup is put in the new_app directory. Enter that folder and start developing like so:

  cd new_app
  npm install
  npm run dev

Visit http://localhost:5000 and open your editor of choice to take a look around of the files that produced the web application. Find src/App.svelte and make a change. It should immediately be reflected in the browser.

The svelte compiler is configured in rollup.config.js and src/main.js is the starting point for the application.

Components

Components in Svelte follows the “component tree” abstraction made popular by React. Creating custom elements that’s inserted into your regular HTML markup. Create a new file src/TimerButton.svelte.

<script>
  // javascript goes here
</script>

<style>
  // styling goes here
</style>

<button on:click="{handleClick}">+ 30 seconds</button>

Remove most of src/App.svelte and import the TimerButton component:

<script>
  import TimerButton from "./TimerButton.svelte"
</script>

<TimerButton />

By now, if your editor hasn’t shown you nice colors yet, it’s time to set it to highlight html and you should be good to go.

The timer button should be disabled for 30 seconds after clicked, so let’s add some logic to do that. It’s a simple interval counting down and we use curly braces to use variables and functions in the markup.

<script>
  let time = 0;

  setInterval(() => {
    // if time is 0 (or below?) stay 0, otherwise decrease by 1
    time = time <= 0 ? 0 : time - 1;
  }, 1000);

  // handler to refer to from the markup
  const handleClick = () => {
    time += 30;
  };
</script>

<style>
  button[disabled] {
    background: white;
    color: lightgrey;
  }
</style>

<button disabled={time !== 0} on:click={handleClick}>+ 30 seconds</button>

If copy pasted correctly, you should have a button which when clicked disables itself for 30 seconds. But it would be nice to show the count down as well. That’s easy to print in some more markup (try and do that now…). But I would like to show how that state can be handled by a store instead.

Stores

Our store will for this example be very simple. There are of course more complex variants (read about them here). Create src/TimeStore.js and fill it with these two lines:

import { writable } from "svelte/store"

export const timeStore = writable(0)

Our TimerButton.svelte should now be refactored to use the store instead of the local time variable. The subscribe part of the code seems somewhat boilerplatey, feel free to rewrite using an auto subscription.

import { timeStore } from "./TimeStore.js"
let time

setInterval(() => {
  timeStore.update(t => (t <= 0 ? 0 : t - 1))
}, 1000)

const handleClick = () => {
  timeStore.update(t => t + 30)
}

timeStore.subscribe(t => {
  time = t
})

We have now created a store and interacted with it using the update and subscribe functions. In fact, the API you need to learn isn’t much more than that. However, we just used a writable store. There are readable and derived stores as well, which we will not get in to more detail about here.

We will create a second component to show the decreasing time value which it will read from the store. This time we will use the much sleaker “auto subscribe” method to read the value. Create src/TickerView.svelte containing the following code:

<script>
  import { timeStore } from "./TimeStore.js"
</script>

<style>
  p {
    font-size: 50px;
  }
</style>

<p>{$timeStore}</p>

This component doesn’t do much more than showing the decreasing number from the store. It includes some CSS though. Styling with CSS in Svelte is isolated to the component that declares the style. Svelte does this by rewriting all style declarations with a unique CSS class. So CSS for a p in one component does affect any other components.

Summary

So far we’ve made one of the simplest applications possible. But believe me, the complexity doesn’t really grow when scaling up. From my experience complexity grows due to poor naming and stress. A framework can only go so far in staying away from the developer’s attention, and in the case of Svelte, I think it has a good level of abstraction.

There are many features in Svelte which we didn’t cover, for instance reactivity, events, bindings, animations 😲, component lifecycle hooks and much more. Since Svelte only brings along those features needed when compiling the code, there’s no need to prioritize away features with regards to bundle size (which other frameworks must compromise).

Resources