Svelte journey | Templating, Reactivity
Denys Sych
Posted on December 22, 2023
Hello!
Svelte has been in my vision scope for quite some time, but it wasn't until today that I actually started exploring it. Finally, I decided to dig it further, triggered by another encounter with yet another post titled "Svelte changed my life". I bet a lot of people heard about it but didn’t start for some reason. Well, this article may be the one!
I may draw parallels to well-known approaches/tooling (mostly React or Angular) when applicable and interject with my own commentary.
Of course, I highly recommend referring to the official tutorial, it is great. However, my aim is to distill the most relevant information and incorporate some thoughts that crossed my mind, you may find this also helpful.
In the context of this series, our goal is to grasp the fundamentals of Svelte as a UI library and SvelteKit as a comprehensive toolkit that equips us with a mature set of tools to construct outstanding applications.
We'll commence with the Basics and delve into Reactivity in this article. You might have already heard about reactivity in Svelte, and it's truly amazing. Still, Svelte team prepares some updates regarding that (Runes, signals).
In this chapter, we embark on our journey together. So, let's delve in.
Templating and basic stuff
<script>
import Nested from './Nested.svelte';
let count = 0;
function increment() {
count += 1;
}
</script>
<p>This is a paragraph.</p>
<Nested />
<button on:click={increment}>
Clicked {count}
{count === 1 ? 'time' : 'times'}
</button>
<style>
p {
color: goldenrod;
font-family: 'Comic Sans MS', cursive;
font-size: 2em;
}
</style>
It looks pretty much the same as the well-known JSX, so don't expect anything too spicy at this point.
However, there are some perks that I would like to outline:
Shorthand attributes
<img {src} alt="{name} dances." />
— no need for src={src}
, just {src}
is enough.
Code placement
Styles, code, and template in a single file. Code should be wrapped in <script />
, and styles in <style />
.
Handlers notation
<button on:click={increment}>
— not onClick
.
Plain string HTML embed
If you need to render HTML stored in a plain string, then use <p>{@html string}</p>
. Keep in mind that there is no sanitization. Dangerous HTML stays dangerous 🔫.
Reactivity 💲
As mentioned at the beginning (and as everyone constantly emphasises), reactivity in Svelte is a pure gold. Still, some updates are coming in Svelte 5.
// Example of basic reactivity usage
<script>
let count = 0;
$: doubled = count * 2; // Kind of like a selector
$: if (count >= 10) { // Kind of a Conditional Effect
alert('Count is dangerously high!');
count = 0;
}
$: console.log(`The count is ${count}`); // Kind of a one-line Effect
function increment() {
count += 1;
}
</script>
<button on:click={increment}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
<p>{count} doubled is {doubled}</p>
Crucial parts, in my opinion, are as follows:
$
Reactive declarations (something like selectors)
let count = 0;
$: doubled = count * 2; // This code `$: doubled = count * 2` re-runs whenever any of the referenced values change.
Why not use plain variables for the same purpose?
Variables (let
, const
) always keep the same initial constant values if you try to make them like $
.
For example:
let a = 1;
let b = a * 2;
$: c = a * 2;
function foo() { a++ }
When foo
is called: a
and c
changes, but b
remains the same. You can do assignment of b
inside the function to change it, but that is another story, which we’ll get to in a minute.
Calling order
Reactive declarations and statements run after other script code and before component markup is rendered.
$
Reactive statements (looks like effects)
Automatic calls happen when the dependency is updated, which feels really good.
// one liner
$: console.log(`The count is ${count}`);
// group multiple lines
$: {
console.log(`The count is ${count}`);
console.log(`This will also be logged whenever count changes`);
}
// with operator (if)
$: if (count >= 10) {
alert('Count is dangerously high!');
count = 0;
}
$
Reactive stuff and referential types
Mutations
push
, splice
, and similar mutation operations won't automatically cause updates. If you want to change something, then an assignment should happen.
// Ugly way
function addNumber() {
numbers.push(numbers.length + 1);
numbers = numbers; // Apply update via assignment
}
// [...pretty, way]
function addNumber() {
numbers = [...numbers, numbers.length + 1];
}
Assignments to properties of arrays and objects, e.g., obj.foo += 1
or array[i] = x
, work the same way as assignments to the values themselves. So, as with plain old assignment for let
or const
, values inside properties can be modified without reassignment.
<script>
let foo = { bar: 2 };
let oof = foo;
function handleClick() {
foo.bar = foo.bar + 1; // This will update {foo.bar}
foo.bar = oof.bar + 1; // This will also update {foo.bar}
// Uncomment next line to make {oof.bar} be updated
// oof = oof;
}
</script>
<button on:click={handleClick}>
foo.bar: {foo.bar} | oof.bar: {oof.bar}
</button>
A simple rule of thumb: the name of the updated variable must appear on the left hand side of the assignment.
For example:
// Won't trigger reactivity on obj.foo.bar
// unless you follow it up with obj = obj.
const obj = { foo: { bar: 1 } };
const foo = obj.foo;
foo.bar = 2;
By the way, you may be in the same situation as me: when I came across this code sample in the tutorial, I interpreted it as something like this:
<script>
const obj = { foo: { bar: 1 } };
const foo = obj.foo;
foo.bar = 2;
$: pluckedBar = obj.foo.bar
obj.foo.bar = 100; // This still triggers reactivity
</script>
{pluckedBar} // Shows 100, but I had a feeling that it should stay
After spending some time, I figured out that it is actually this:
<script>
let obj = { foo: { bar: 1 } };
let foo = obj.foo;
$: pluckedBarBeforeReassign = foo.bar
foo.bar = 2;
$: pluckedBarAfterReassign = foo.bar
function doit() {
foo.bar = foo.bar + 1;
/*
* If commented, then {obj.foo.bar} in the template would never update.
* The rest, {foo.bar} {pluckedBarBeforeReassign} {pluckedBarAfterReassign}
* act as expected.
*/
// obj = obj
}
</script>
<button on:click={doit}>
{obj.foo.bar} <!--This guy you should pay attention to-->
{foo.bar}
{pluckedBarBeforeReassign}
{pluckedBarAfterReassign}
</button>
Summary
In this part, we've covered the basics of templating and reactivity. We've followed the Svelte official tutorial, so if you feel excited about Svelte and want to have a broader look, it is a great idea to visit their interactive tutorial. My goal here is to share with you the most crucial information with extended comments on tricky parts, and I hope that I've accomplished that at least to some extent.
Take care, go Svelte!
Posted on December 22, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.