10 Apr 2023
Svelte is a JavaScript framework that compiles your code when you build your app, which results in highly-optimized vanilla JavaScript.
While we mostly use ReactJS for our front-end web development here at Significa, we're always looking for ways to improve our code by testing new tools and frameworks. After playing around with Svelte and doing some projects, it seemed only fair to share my experience with it, how different it felt from ReactJS, and what a React developer can expect when getting into it.
For those who might be out of the loop, Svelte is a JavaScript framework that compiles your code when you build your app, which results in highly-optimized vanilla JavaScript. This means you'll end up with smaller bundle sizes and better performance. Svelte also sticks very close to raw HTML, CSS, and JavaScript (as we'll see further down). All of this might as well justify the fact that Svelte was voted the second most-loved web framework of 2022.
While this post will cover some of Svelte's major features, I invite you to do the Svelte tutorial, as it will fully guide you through everything the framework offers.
"Hello, World!"
, says Svelte. Let me first introduce you to the code before we dive into it.
<script>
let name = 'world'
</script>
<h1>Hello, {name}!</h1>
<style>
h1 {
color: #546659;
font-size: 24px;
}
</style>
Yeah, you're looking at it right. It looks like plain JavaScript, HTML, and CSS. How cool is that?
So, it seems that the Svelte convention is to have your, then your html
and finally your <style>
tag. On your, you'll have most of your logic and state (as expected) and your <style>
will deal with any CSS that you might need (and the coolest part of this is that these styles are scoped to the component). And it's just built-in! For this to work on ReactJS, we would probably need to use css-modules or styled components.
Now, the html
might look like regular HTML, but it's not. There are specific features regarding Svelte, likeif statements
(that look like {#if}
) and map()
(that looks like {#each}
). Personally, I've grown tired of React's conditional rendering since it just blends too well with the rest of the code, so seeing how Svelte deals with that was really interesting. Here's an example showing the same code on React and on Svelte:
// React
{ isDarkMode
? <h1>Dark Mode Zone</h1>
: <h1>Light Mode Zone</h1>
}
// Svelte
{#if isDarkMode}
<h1>Dark Mode Zone</h1>
{:else}
<h1>Light Mode Zone</h1>
{/if}
While the React code looks more compact, I personally feel like our conditions end up extremely messy when we have a lot of logic on them (like several conditions and then nested ones) and the simplicity of Svelte here somewhat baffled me. While it does increase the number of lines, I'm now fully aware of what I'm doing and where the if/else
starts and ends.
I hope that this short intro to Svelte's structure was enough to entice you (remember, there's always Svelte's tutorial to dive deeper) because we're jumping into...
In 2018 we were introduced to Hooks on React, which completely revolutionized the way we do code. However, sometimes, we end up battling with our own useState
and its updates, and I'm fairly sure that everybody working with React got their fair share of headaches with it (let's throw useEffect
and useMemo
into the mix while we're at it).
Svelte deals with our state in a simpler way. Our useState
from React is a simple variable assignment on Svelte. Here's a comparison:
// React
import { useState } from 'react'
export default const Counter = () => {
const [count, setCount] = useState(0);
return (
<>
<h1>Count at: {count}</h1>
<button onClick={() => setCount(count++)}>Increase</button>
<button onClick={() => setCount(count--)}>Decrease</button>
</>
)
}
// Svelte
<script>
let count = 0;
</script>
<h1>Count at: {count}</h1>
<button on:click={() => count++}>Increase</button>
<button on:click={() => count--}>Decrease</button>
Svelte will simply update your code! 🪄
In React, we have several options to deal with state that is controlled/listen to on different components (like Redux, useContext and useReducer) and they are great at what they do.
Svelte has two approaches to its state.
For simpler states, Svelte has its Context API, which makes the state available to the component and its descendants. This basically means that you don't have to prop drill from aParentto several Grand-grand-children. Instead, you can use Context. Context API has two built-in functions: getContext
and setContext
. As the names clearly say, getContext
will retrieve the values, and setContext
sets the values. With this, we have a great way of managing the state of components within the same component tree.
There are times when local and component-tree states just can't do the job and Svelte has Stores to deal with it. Stores are objects with a special method called subscribe
that allows components to be notified when one of the values of these objects changes. Svelte has two types of stores: writable
ones and readable
ones.
Writable stores create objects with values that can be set from components subscribed to them. It is accompanied by two methods: set
and update
. set
will overwrite the value set on the store while the update
will receive a callback function as an argument and will take the existing value on the store as its argument and, well, update it. The following code shows the application of set
and updates
with two files, store.js
and App.svelte
(the code can also be found and played with here).
// store.js
import { writable } from 'svelte/store';
let counter = writable(1);
export { counter };
// App.svelte
<script>
import { counter } from './store.js'
function incrementCounter() {
counter.update(n => n + 1);
}
function setCounter() {
counter.set(2)
}
</script>
<h1>{$counter}</h1>
<button on:click={incrementCounter}>Increment Counter</button>
<button on:click={setCounter}>Set counter as 2</button>
Notice the dollar sign, $
, used when calling counter
. That's how you can access the values of your store!
Like writable stores, readable stores also have objects in them. While they can not be updated by external components, they can be updated within the store. This means that readable stores are great for dealing with immutable data that you still need to access from several spots on your app! You work with them by creating your store
using the readable
method and then you access them using $
, as we've seen in the example above (here's the live example).
// store.js
import { readable } from 'svelte/store'
let startingSeconds = 0;
export const seconds = readable(startingSeconds, set => {
setInterval(() => {
set(startingSeconds++)
}, 1000)
})
// App.svelte
<script>
import { seconds } from './store'
</script>
<h1>{$seconds} have passed</h1>
These are, for me, the starting bricks of Svelte, but there's so much more to it, like SvelteKit, (and quirky things like two-way data binding) and, honestly, the more I play with Svelte, the more I get marvelled by how easy it is to work with it and get something up-and-running. I'm surely going to keep using it!
Rui Sousa
(Former) Front-end Developer
Significa
Team
30 September 2024
Optimise your e-commerce website for better performance.Ricardo Reis
(Former) Front-End Developer
Francisco Marques
CTO