Svelte is an open-source framework that has attracted interest since it released version 3 in 2019.
It is the best front-end framework for interest and satisfaction in the State of JS survey.
In short, it’s a UI framework that compiles your components into highly optimized vanilla JavaScript.
The Svelte maintainers have also developed SvelteKit – the successor to Sapper – to build web applications with offline support, prefetching, or server-side rendering features.
One of the key features is its small bundle size which is therefore often used in embedded devices due to its limited storage capabilities.
It is also known for its simplicity and ease of use.
There is a detailed tutorial on the official Svelte website that explains all the topics described here in more detail.
There are also some examples from this tutorial being used here. This introduction is intended to give a rough overview of Svelte.
Setup
The easiest way to set up a Svelte Project is to use a bootstrap script, which creates all necessary files. The most common one is by using degit
.
# Setup project (mandatory)
npx degit sveltejs/template my-svelte-project
# Install Typescript (optional)
cd my-svelte-project
node scripts/setupTypeScript.js
# Install all packages (mandatory)
npm install
Another option is to use vite
.
npm create vite@latest my-svelte-app -- --template svelte
npm create vite@latest my-svelte-ts-app -- --template svelte-ts
The difference between these two is the build technology that is being used. With degit
it will be set up with rollup and with vite, as the name already suggested, uses vite.
Structure
Svelte is component-based. Such a component consists, as in other known frameworks, of a script part, where the behavior can be implemented in Javascript or Typescript. Then followed by the HTML part and finally comes the CSS. The special feature here is that the style tag for each component is always scope-based. That means classes in Parent.svelte
have no influence on other components even in their child components. For global CSS a separate .css file must be created and imported into the upper svelte component in the script
tag.
Event Handling
DOM Events
As usual with other frameworks, DOM event handling is quite simple. For simple cases, it is possible to use inline handlers. For more complex functions, it is better to use a separate component method.
<script>
let m = { x: 0, y: 0 };
function handleMousemove(event) {
m.x = event.clientX;
m.y = event.clientY;
}
</script>
# Component function
<div on:mousemove={handleMousemove}>
The mouse position is {m.x} x {m.y}
</div>
# Inline
<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
The mouse position is {m.x} x {m.y}
</div>
Component Events
As with other frameworks, there are ways for communication between different components. One of them is via events. As you can see in the example below, you need to create an event dispatcher which in this example is triggered within the sayHello
function that is called by a click on the button to send the message
event.
# Child.svelte
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Say Hello
</button>
# Parent.svelte
<script>
import Child from './Child.svelte';
const handleMessage = (event) => {
alert(event.detail.text);
}
</script>
<Child on:message={handleMessage}/>
An alternative for not using component events to avoid boilerplate code is to pass the function as a prop and call it instead of emitting an event. To define a prop in the child component instead of simply declaring a variable with let alertFromChild
, you must add the export
keyword.
# Parent.svelte
<script>
import Child from './Child.svelte';
const alertFromChild = () => {
alert('Alert from child component');
}
</script>
<Child {alertFromChild} />
# Child.svelte
<script>
export let alertFromChild;
</script>
<button on:click={alertFromChild}>
Trigger Alert
</button>
Forms
Svelte offers an easy way to bind values in e.g. input fields. Instead of using a event listener on the input element and creating a function that updates the name
variable, we can just use bind:value
. value
refers to the field in Event.target.
<script>
let name = 'world';
</script>
<input bind:value={name}>
<h1>Hello {name}!</h1>
State
The state handling stays in the theme of the framework, which is simplicity. We don’t need to use hooks like useState
in React, we just declare a variable and use or update it and Svelte handles the rendering for us. If states are based on other states, doubled
in the below example, simply using $:
instead of let
will solve this. doubled
will automatically be updated whenever count
changes.
let count = 0;
$: doubled = count * 2;
<p>{count} doubled is {doubled}</p>
Templating
To render HTML based on conditions or loops, we can wrap the HTML in if/else
or each
blocks.
# if-condition
{#if x > 10}
<p>{x} is greater than 10</p>
{:else}
{#if 5 > x}
<p>{x} is less than 5</p>
{:else}
<p>{x} is between 5 and 10</p>
{/if}
{/if}
# loop
let things = [
{name: 'mouse'},
{name: 'keyboard}
];
{#each things as thing}
<Thing name={thing.name}/>
{/each}
Lifecycle Hooks
Here is a quote from the Svelte Tutorial that explains lifecycle hooks and their use pretty well:
Every component has a lifecycle that starts when it is created and ends when it is destroyed. There are a handful of functions that allow you to run code at key moments during that lifecycle.
The one you’ll use most frequently is
onMount
, which runs after the component is first rendered to the DOM.It’s recommended to put the
svelte.dev/tutorial/onmountfetch
inonMount
rather than at the top level of the<script>
<script>
import { onMount } from 'svelte';
let photos = [];
onMount(async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);
photos = await res.json();
});
</script>
Transitions
One of the quality of life and very easy to use feature is the transition
directive. This allows us to create effects more easily and therefore create more appealing user interfaces by gracefully transitioning elements into and out of the DOM.
Svelte makes this very easy with the transition directive. Here is a simple example of how to implement a fade on a paragraph with
the fade
function from svelte/transition
.
<script>
import { fade } from 'svelte/transition';
let visible = true;
</script>
<button on:click="{() => visible = !visible}">
fade
</button>
{#if visible}
<p transition:fade>
Fades in and out
</p>
{/if}
So why should we use Svelte over other frameworks?
A decisive advantage is that many things are kept simple. There is little code to write because there is little boilerplate code. This also leads to fewer bugs, and it is also much easier to maintain when components consist of fewer lines of code.
As mentioned at the beginning, the size of the bundle can also play a big role in the evaluation. In comparison, Svelte was only 3.8kB big, compared to the React at 6.3kB, Vue (63.7kB) or Angular (180.3kB).