Understanding Svelte – The (very) basics
I have to admit, Svelte caught my attention.
It takes a totally different approach than other JavaScript UI libraries or frameworks for keeping the UI up-to-date with changes triggered by data changes or user interactions. Instead of re-rendering the appropriate segments of the UI at runtime, it prepares the work at build time – Svelte is a compiler.
If you're intrigued –as I was– go and have a look at the Svelte home page. There's also an outstanding REPL where you can dive right in and play around with some code. I'll put some more links at the bottom of this post for further where you can learn more.
You can also stay here for a few more minutes, and read about how I'm learning Svelte. This is just the first, short post in the series I'm planning to write as I'm learning what seems like a fascinating library. I think it's gonna be educational for you, too, as I've probably made (and will make) the same errors other people would make.
Component-focused
Components are what make Svelte is all about. That makes a ton of sense, of course, and doesn't surprise someone coming from another view library like React. However, it was a bit of a mind shift for me coming from Ember.js, a full-fledged SPA framework.
Also, all pieces that make up a component –the JavaScript, the styles, and the template– is placed in one single file with a .svelte
extension.
I decided to build a relatively simple "app" as my first learning example that can gradually be extended in lots of ways to understand other bits of Svelte.
What I came up with is a crypto card that at first only displays the current price of a crypto currency. Later, I'll add historical prices and price changes, the possibility to update the current price, add other currencies, and so on.
So the first milestone is just to have the real price displayed:
Component props
I created a file called CryptoCard.svelte
and pasted in the following:
// src/CryptoCard.svelte
<div class="card">
<label>BTC</label>
<div class="icon-holder">
<IconRefresh fill="#CBD5E0" stroke="#CBD5E0" strokeWidth="0.1"/>
</div>
<span>$10,209</span>
</div>
Let's see the `IconRefresh` component:
// src/IconRefresh.svelte
<script>
export let fill;
export let stroke;
export let strokeWidth;
</script>
<svg viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="{fill}"
stroke="{stroke}"
stroke-width={strokeWidth}
>
<path d="M8.52 7.11a5.98 5.98 0 018.98 2.5 1 1 0 11-1.83.8 4 4 0 00-5.7-1.86l.74.74A1 1 0 0110 11H7a1 1 0 01-1-1V7a1 1 0 011.7-.7zm5.51 8.34l-.74-.74A1 1 0 0114 13h3a1 1 0 011 1v3a1 1 0 01-1.7.7l-.82-.81A5.98 5.98 0 016.5 14.4a1 1 0 111.83-.8 4 4 0 005.7 1.85z"/>
</svg>
The props of the component that can be passed in from the outside need to be exported. Here, all of fill
, stroke
and strokeWidth
are such props. When CryptoCard
calls IconRefresh
, it passes in all of these.
We can define default values for component arguments just by assigning them a value at definition time:
// src/IconRefresh.svelte
<script>
export let fill;
export let stroke;
export let strokeWidth = 1;
</script>
strokeWidth
now has a default value of 1 which can be overridden by passing it in, like in the above example.
Back in our code, we want to be able to use IconRefresh
so we need to import it:
// src/CryptoCard.svelte
<script>
import IconRefresh from './IconRefresh.svelte';
</script>
<div class="card">
<label>BTC</label>
<div class="icon-holder">
<IconRefresh fill="#CBD5E0" stroke="#CBD5E0" strokeWidth="0.1"/>
</div>
<span>$10,209</span>
</div>
Notice how we wrapped the JavaScript import in a <script>
tag. All of the JavaScript code in our components will live inside it.
CSS
The card doesn't look anything like our original design so we need to add the necessary styles. They need to be placed inside <style>
tags:
// src/CryptoCard.svelte
<script>
import IconRefresh from './IconRefresh.svelte';
</script>
<style>
.card {
display: flex;
flex-direction: row;
justify-content: flex-start;
height: 4rem;
width: 16rem;
background-color: #5A67D8;
align-items: center;
padding-left: .8rem;
padding-right: .8rem;
}
label {
color: #F7FAFC;
font-size: 1.6rem;
}
.icon-holder {
height: 24px;
width: 24px;
margin-left: auto;
cursor: pointer;
}
span {
font-size: 1.2rem;
color: #F7FAFC;
}
</style>
<div class="card">
<label>BTC</label>
<div class="icon-holder">
<IconRefresh fill="#CBD5E0" stroke="#CBD5E0" />
</div>
<span>$10,209</span>
</div>
That's more like it! It's important to keep in mind that styles in a Svelte component are local to that component and thus don't leak. Svelte achieves this by auto-generating class names and applying it both to the rules and the elements they apply to:
We have now recreated the original small crypto tab.
It doesn't even have a real price, though, so we'll make it so in the next post and learn a thing or two about reactivity in Svelte.
In the meantime, some Svelte links:
- The Svelte REPL
- What is Svelte? – Could be called "Svelte in 1 minute". A very short post, a gist, actually.a
- Svelte 3 – Rethinking reactivity – Includes a video about the presentation the author, Rich Harris, gave when announcing version 3 of Svelte.
- Frameworks without the framework: why didn't we think of this sooner?
- Metaphyics and JavaScript – An insightful, mildly provocative presentation from Rich Harris. These are the slides, with verbose speaker notes.