For those who don’t know what svelte is, it’s a component framework similar to React or Vue. It’s faster, reactive and has less boilerplate.
Svelte runs at build time, converting your components into highly efficient imperative code that surgically updates the DOM. As a result, you’re able to write ambitious applications with excellent performance characteristics.
INTRODUCTION
In this tutorial, we are going to create a simple Chuck Norris jokes generator. We will be using Chuck Norris API to request a random joke. We will also provide a button for the user to fetch a new joke.
Let’s start by creating a basic svelte app, I prefer using npx instead of downloading the CLI on my computer. Go ahead and start the project using:
npx degit sveltejs/template quote-generator
cd quote-generator
npm install
This will generate a bare minimum project for svelte with App.svelte
and main.js
. Main.js is our entry point for the project and App.svelte is the Svelte component. Let’s run our project using npm run dev
.
We currently have Hello World in App. Let’s remove everything and start fresh. On the script part let’s create a function to get a random joke.
<script>
const URL = "https://api.chucknorris.io/jokes/random";
const fetchJoke = async () => {
const response = await fetch(URL);
const data = await response.json();
};
</script>
The fetch joke function will call the API to get a random joke each time. Now we have the joke inside data variable. But it’s scope is within fetchJoke
function and is not accessible from outside. So, let’s create a global variable called joke
and once we finish fetching from api we will update this variable with a new joke.
<script>
import { onMount } from "svelte";
const URL = "https://api.chucknorris.io/jokes/random";
let joke = "";
const fetchJoke = async () => {
const response = await fetch(URL);
const data = await response.json();
joke = data.value;
};
onMount(fetchJoke);
</script>
I am adding a lifecycle from svelte called onMount, it provides us the information that the window is loaded and the component is mounted to DOM. So, once it’s ready we will call the fetchJoke function which will get the data and assign a new value to the joke variable. It’s important for joke variable to be in global scope so that our markup has access to it. Let’s go ahead and create a div with a joke and a button.
<style>
#main-app {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
}
#joke {
font-size: 24px;
margin-bottom: 20px;
}
</style>
<div id="main-app">
<div id="joke">{joke}</div>
<button>Refresh Joke</button>
</div>
Let’s add this code after the script tag. This has some basics styles to place the joke and button on the center. You can see how easily we can access the joke variable in our markup by using {job}. In svelte, we don’t need to use setState or any other method to re-render. It’s reactive to the value of joke. If the value changes it automatically updates the HTML part of it. Now let’s bind the event handler to bind click event on the button.
<button on:click={fetchJoke}>Refresh Joke</button>
That’s it. It’s as simple as that. Now the button handles the click event and calls the fetchJoke function which modifies the joke variable and our html gets updated. Let’s modify the app further by having a loading indication and disabling the button while fetch is in progress.
I am going to create a loading variable similar to joke and set it as false initially. When the fetchJoke function is called it should change it’s value to true and once it’s done the value changes back to false. Since svelte is reactive we don’t have to worry about if the re-render is batched or delayed like in React when you use setState. You can read about the problems of setState here. Here’s the final code:
<script>
import { onMount } from "svelte";
const URL = "https://api.chucknorris.io/jokes/random";
let joke = "";
let loading = false;
const fetchJoke = async () => {
loading = true;
const response = await fetch(URL);
const data = await response.json();
joke = data.value;
loading = false;
};
onMount(fetchJoke);
</script>
<style>
#main-app {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
}
#joke {
font-size: 24px;
margin-bottom: 20px;
}
</style>
<div id="main-app">
<div id="joke">{loading ? 'Loading...' : joke}</div>
<button on:click={fetchJoke} disabled={loading}>Refresh Joke</button>
</div>