Back

A Practical Introduction to Svelte

A Practical Introduction to Svelte

Hey, and welcome to this tutorial! If you’re here, you’ve probably heard of Svelte before and wanna know what all the fuzz is about. Luckily I’m here to walk you through the basic concepts of Svelte, by building a real-world project that I think you’ll really like! So without further ado, let’s get started with a primer on what Svelte even is:

What is Svelte?

Svelte is a “radical new approach to building user interfaces”, according to the official documentation. In practice, Svelte is quite similar to JavaScript frameworks like React, Vue and Angular - it’s a way to create websites and web applications using primarily JavaScript. Like these frameworks, Svelte is also based around the idea of components: small “chunks” of code that encapsulates template and logic, to create reusable “building blocks”.

A classic example of a component could be a navigation menu, which is usually present on every page on the website. Having it as a component makes sense, to encapsulate all the related code in one place for easy modification in the future. To accomplish this, Svelte borrows the idea of SFC’s (Single File Components) from Vue. The idea is to write all your logic in a <component-name>.svelte file, which combines all the necessary HTML, CSS and JS for that component.

While all the major frameworks use components as their key abstraction, Svelte differs from them in a few key areas:

  1. Svelte is actually a compiler rather than a framework. Meaning that while you develop you code in a similar fashion to other frameworks, Svelte will compile it into vanilla JavaScript for the browser to use.
  2. Svelte tries to stay close to the native platform of the web. It does this by leveraging “standard” HTML, CSS and JS as much as possible.

These two points are what makes Svelte exciting to me. It’s a breath of fresh air, and because Svelte is a compiler, it can include a lot more features “out-of-the-box” since it doesn’t have to worry about shipping everything to the browser. That’s why Svelte comes with built-in animations, a global store and many other great features.

Let’s explore Svelte by building a real-world project: A Cryptocurrency tracker!

What we’ll be building

We’re gonna build an overview page of the most popular cryptocurrencies, by fetching some data from an external API, and displaying it using different Svelte features. This will allow us to get familiar with:

  • Svelte components (using .svelte files)
  • Iterating over an array to display some data
  • If/else statements in Svelte
  • Handling button clicks from the user
  • Styling Svelte components
  • Dynamically binding classes based on data

Our finished project will end up looking like this:

I’ll also include a list of extra feature ideas for you to play around with and implement yourself, to get even more familiar with Svelte.

Installation and setup

So, before we get started, there’s a few requirements: You must have NodeJS installed, and access to a terminal/shell. You’ll also need a code editor. I recommend VS Code, as it comes with a built-in terminal, but you can use whatever you like.

With those in place, open up a terminal and create a new project using the official Svelte starter template:

npx degit sveltejs/template svelte-crypto-tracker

npx comes built-in with NodeJS, degit is a project scaffolding tool, sveltejs/template is the official template, and svelte-crypto-tracker is the name of our project (feel free to rename it). When that’s done, you want to cd into the project:

cd svelte-crypto-tracker

Then, install the dependencies and start the project in development mode:

npm install
npm run dev

Open up localhost:5000 in your browser to see the official welcome page:

When you see this, you’re up and running! Now open the code folder in your editor, and let’s get started on our app.

Getting started

In the src folder, there’s an App.svelte component. This is our main component that the template has generated for us, and where we’ll be writing most of our logic. Every .svelte file consists of 3 parts (not necessarily in this order):

  • A script block, containing all the JavaScript logic
  • A style block, where we can write CSS that will be scoped to this component
  • And finally, the rest of the file becomes our template, where we write HTML and use special Svelte features to manipulate it

By default, we’re showing Hello {name}, where name is a property passed to our component from somewhere else - in this case, the main.js file responsible for bootstrapping our app. We can see that name is declared as a variable in the script tag, with export let name.

In Svelte, any variable defined in the script block is available to use in the template. And if we put export in front of it, that allows other components to pass a property down into this variable. Start by removing the export and setting the variable to “Crypto Tracker” instead, and then simply display that in the template:

// src/App.svelte
<script>
  let name = "Crypto Tracker";
</script>

<main>
  <h1>{name}</h1>
</main>

Great! There’s really not much else to variables in Svelte, and this simplicity is one of the reasons I enjoy working with it.

Let’s add a button too, that we’ll use to fetch the coin data from an API in a moment. We’ll do that by adding a special on:click directive that Svelte gives us, and tell it to call a function when that happens:

// src/App.svelte
<script>
  let name = "Crypto Tracker";

  function fetchCoins() {
    console.log("Hello world!");
  }
</script>

<main>
  <h1>{name}</h1>
  <button on:click={fetchCoins}>Fetch Coin Data!</button>
</main>

Click the button and check your console to verify that it works - you should see “Hello world!“. Now let’s get to the meat, and actually fetch some data and show it on the screen.

Fetching and displaying data

We’re gonna get our data from the Coinstats API, so let’s update our code by adding a new variable, coins, and setting it to an empty array - and then change our fetchCoins function to fetch some data (all in the script tag):

// src/App.svelte
<script>
  let coins = [];

  async function fetchCoins() {
    const response = await fetch(
      "https://api.coinstats.app/public/v1/coins?skip=0&limit=20"
    );
    const data = await response.json();
    coins = data.coins;
    console.log("coins:", coins);
  }
</script>

There’s a couple of things going on in our function, so lets walk through it. Firstly, we changed our function to be async, and then call the native browser fetch function, passing an URL to the API endpoint we get from the API documentation. This particular endpoint will fetch the first 20 coins. Try to open the link in your browser to see what kind of data we’re getting back.

We then save this in a response variable, and call the .json() function on it, to get the data in a format we can work with. Finally, we update our coins array with the fetched coin data, and log it to the console. It should look something like this:

Awesome! We got some data, now let’s show it on the page. We can do this using the svelte {#each} block. This takes an array (our coins array), and iterates over it, giving us access to each individual coin object inside the array. Use it like this:

// src/App.svelte
<main>
  <h1>{name}</h1>
  <button on:click={fetchCoins}>Fetch Coin Data!</button>
  {#each coins as coin}
    <p>{coin.name}</p>
  {/each}
</main>

That’ll show 20 coin names on our page - neat! But just showing the name isn’t that exciting, we also want to see stuff like the current price and recent percentage changes in the price. So for each coin, we’re gonna make a new component called CoinCard.

In the src folder, create a new file called CoinCard.svelte. Put the following code in the file:

// src/CoinCard.svelte
<script>
  export let coin
</script>

<div>
  {coin.name} - {coin.symbol}
</div>

The export let coin tells Svelte that we expect a coin prop to be passed to this component. Let’s switch back to App.svelte and use our new component by importing it at the top of our file, right below the opening script tag:

// src/App.svelte
import CoinCard from "./CoinCard.svelte"

And then we’ll use it inside our loop, by replacing

// src/App.svelte
{#each coins as coin}
  <p>{coin.name}</p>
{/each}

with

// src/App.svelte
{#each coins as coin}
  <CoinCard {coin} />
{/each}

Sweet! Now we’re still iterating our coins array, and for each coin, we’re rendering our CoinCard component, passing down the data for a particular coin as a prop. The {coin} part of the above code is actually a shorthand for writing coin={coin}, with the first coin being the name of the variable in the CoinCard component (that we declared using export let coin), and the second coin being the actual value from the array. If we wanted to, we could pass other data by adding something like hello={42}, and if the CoinCard exported a variable called hello, it would receive the value 42.

Showing more data and styling

Let’s show some more data than just the coin name and symbol. Replace the HTML with:

// src/CoinCard.svelte
<div class="coin-card">
  <img class="coin-image" src={coin.icon} alt="" />
  <p class="coin-name">{coin.name}</p>
  <div class="coin-details">
    <div
      class="detail-field price-change"
    >
      {coin.priceChange1w}%
    </div>
    <div class="detail-field">{coin.price.toFixed(2)}$</div>
    <a class="detail-field" href={coin.websiteUrl}>Website &#8594;</a>
  </div>
</div>

This will show the coin icon, name, price change over the last week, the current price (fixed to 2 decimal points using the JS function toFixed()), and a link to the coins website. This is great information, but it doesn’t look good.

Let’s fix that. Another cool thing about Svelte is that we can write CSS directly in our component using the style tags, and this CSS will only apply to elements inside this component. I’ve written a bunch of CSS for us here to make the CoinCards look a lot better. Paste the below code in your CoinCard.svelte file, on the bottom:

// src/CoinCard.svelte
<style>
  .coin-card {
    background-color: white;
    border-radius: 20px;
    --tw-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1),
      0 10px 10px -5px rgba(0, 0, 0, 0.04);
    box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
      var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
  }

  .coin-image {
    max-width: 4rem;
    margin-top: 10px;
  }

  .coin-name {
    font-size: 24px;
    font-weight: bold;
    margin-top: 15px;
  }

  .coin-details {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    border-top: 1px solid #d1d5db;
    background-color: #f3f4f6;
  }

  .detail-field {
    font-size: 15px;
    font-weight: bold;
    padding: 15px 0;
  }

  .detail-field:not(:last-child) {
    border-right: 1px solid #d1d5db;
  }

  .price-change {
    color: #dc2626;
  }

  .positive {
    color: #16a34a;
  }

  @media (min-width: 1024px) {
    .detail-field {
      font-size: 18px;
      padding: 15px;
    }
  }
</style>

I won’t walk through what all the styles do, but it’s mostly organizing the content in a grid and changing font sizes/colors/margins. We’ll also need to adjust some things in App.svelte, so go back to that file and add these styles:

// src/App.svelte
<style>
  main {
    text-align: center;
    padding: 40px 0;
    margin: 0 auto;
  }
  h1 {
    color: #ff3e00;
    text-transform: uppercase;
    font-size: 4em;
    font-weight: 100;
  }

  .grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
    gap: 30px;
  }

  @media (min-width: 640px) {
    main {
      max-width: 1600px;
      padding: 40px 20px;
    }
  }
</style>

And also, let’s wrap our iterating loop in a grid class:

// src/App.svelte
<div class="grid">
  {#each coins as coin}
    <CoinCard {coin} />
  {/each}
</div>

Head back to the app, and it should look something like this:

Much better, right? Now we’re actually showing useful information in a nice grid overview. There’s still a few things to do, so let’s tackle those next.

Adding some details

As you might have noticed, all the price changes (shown in percentage) are currently colored red. But wouldn’t it be nice if they were colored green to indicate a positive increase, and only red if the price has decreased? This is actually quite easy using Svelte’s class directive.

Back in CoinCard.svelte, let’s add a function to determine if the price change is positive or negative. Paste the code right below the previous export let coin in the script tag:

function changeIsPositive(priceChange) { 
  return priceChange >= 0;
}

This function will take a number (the priceChange) and check if it’s greater than or equal to 0. If it is, our function will return true (and false if the number is negative).

Now that we have a way to figure out if the priceChange is positive, we can add a dynamic class binding. Find the div element with a class of detail-field price-change, and add the following attribute to the element:

class:positive={changeIsPositive(coin.priceChange1w)}

This special Svelte directive will add the positive class if the changeIsPositive function returns true when passed the coin’s weekly price change, and false otherwise. The positive class is defined in the style tag, and simply makes the text green:

.positive {
    color: #16a34a;
}

Your price-change element should now look like this:

// src/CoinCard.svelte
<div
  class="detail-field price-change"
  class:positive={changeIsPositive(coin.priceChange1w)}
>
  {coin.priceChange1w}%
</div>

Aaaand now our app will show a green number for positive increases:

Almost there! Let’s add one final detail before wrapping up. I’d prefer if we only show the “Fetch Coin Data!” button when no coins have been loaded. And luckily, we can easily accomplish this using Sveltes {#if} block. Head back to App.svelte and update the template to look like this:

// src/App.svelte
<main>
  <h1>{name}</h1>
  {#if coins.length === 0}
    <button on:click={fetchCoins}>Fetch Coin Data!</button>
  {:else}
    <div class="grid">
      {#each coins as coin}
        <CoinCard {coin} />
      {/each}
    </div>
  {/if}
</main>

Here we’re saying, IF the coins array has length 0 (meaning we don’t have any coin data yet), show the button to fetch coins. Otherwise, we have already fetched the coin data, so we show the grid and iterate over the coins instead. To make sure this works, visit the app and press the button. It should disappear as the coins get loaded 👍

Awesome! I think we got a pretty sleek looking app here. However, there’s a lot of other features that would be great to add as well.

Observability for Production Apps

Debugging your JavaScript apps in production may be challenging and time-consuming. OpenReplay is an open-source session replay stack for developers. It helps you replay everything your users do and shows how your app behaves and renders for every issue. It’s like having your browser’s inspector open while looking over your user’s shoulder.

Open Source Session Replay

OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data.

replayer.png

Start enjoying your debugging experience - start using OpenReplay for free.

Features to add

Now that you know the basics of Svelte and have a working app, I encourage you to try adding some more features on your own. Below is a list for inspiration, but feel free to add whatever you can think of - try to make it your own!

  • Add an input field where the user can write a coin name to filter the results
  • Allow the user to fetch more coins with a “Load More” button
  • Make each coin clickable to show more data on that coin
  • Show a graph of price changes for a specific coin
  • Allow the user to sort the coins by percentage growth or current price
  • Deploy the project to Netlify

Conclusion

Thanks for reading! I hope this gave you a nice introduction to Svelte, and that you had as much fun with the project as I did. I really encourage you to add a few extra features and deploy the project to Netlify. You can even add it as a portfolio project on your website if you’d like!

To learn more about Svelte, check out the official tutorial which has lots of great, interactive examples. And keep your eyes out for SvelteKit, which is an upcoming meta-framework on top of Svelte (kind of like NextJS for React) that’s currently in beta.

Finally, I’d love to know your thoughts on Svelte and this article, so hit me up on Twitter at @madsbrodt. Thank you!