I created a Quiz app using Svelte and now I cannot go back to any other framework.
Tags: svelte, webdev, javascript
Published: 2019-11-24 05:28:03

>**Update:** I have updated this project with animations and corrected the logic behind the app. You can find the most up to date version [here](https://github.com/manan30/svelte-trivia) and the live [demo](https://manan30.github.io/svelte-trivia/).

I was hearing about **Svelte** a lot and after listening to this [talk](https://www.youtube.com/watch?v=AdNJ3fydeao) I wanted to give it a try too. So I did and it turns out that Svelte is super amazing. I am used to programming with **React** a lot and some clear differences made me fall in love with Svelte.

## What is Svelte

[Svelte](https://svelte.dev) is a compiler that converts our declarative component-based code into JavaScript that can manipulate the DOM directly. You might have heard that Svelte is blazing fast, faster than any other framework out there and that is true. The reason behind this is because of the very fact that Svelte is more of a compiler than a framework or a library. Svelte does not use a Shadow DOM or a Virtual DOM to perform DOM updates, naturally making it orders of magnitude faster than frameworks or libraries that ship with a virtual DOM implementation. It tries to eliminate a lot of boilerplate code and is truly reactive. If you are coming from a React ecosystem like me, Svelte challenges the way you think in a lot of ways.

---

In this article, we will create a small trivia app and see how Svelte compares to React.

Let's start by creating a Svelte project first. Svelte just like `create-react-app` provides a way to bootstrap a Svelte app. Just run the code below to get up and running.

```bash
npx degit sveltejs/template my-svelte-project
cd my-svelte-project

npm install
npm run dev
```

Our project directory should look something like this.

![directory-structure](https://thepracticaldev.s3.amazonaws.com/i/ey9h6anovx9008pbrbv3.png)

Now, if you open up `package.json` you'll see something amazing.

![package.json](https://thepracticaldev.s3.amazonaws.com/i/eky0amyk3z1kjfsiiu18.png)

It does not have any `dependencies` listed. All the dependencies are `devDependencies`. This is because Svelte is a compiler and all the dependencies are computed beforehand while generating the build and hence our final code doesn't ship with any of those dependencies making our build size much much smaller.

- The `main.js` file is our main entry point into the application. It is like the `App.js` file in a React project.
- We also see the `App.svelte` file. Let's open the file and understand the different parts of it.
- If you are familiar with React we know that we usually end our React specific files with a `.jsx` extension. Similarly in Svelte, all our Svelte specific files end with a `.svelte` extension.
- Each Svelte file contains either just `markup (HTML tags)` or markup with styles enclosed in the `<style></style>` tag or JavaScript enclosed in `<script></script>` tag or all three of them.
- The best part about the Svelte component is that the styles inside of them are scoped to that component only and hence you won't run into an issue where the styles would leak into some other component.
- If you are used to writing **HTML in JS** with `JSX`, Svelte is the exact opposite of that and you write everything in a `svelte` file which is just syntactic sugar for writing `HTML` files.

**Note: If you are coming from a React background you might not be used to thinking this way but believe me this is going to help you expand your boundaries.**

With that being said let's start.

---

First, we are going to look at the `App.svelte` file. This is our main file/component which serves as the entry point for the app. You can use the code below for your reference.

```html
<script>
  // import QuizArea from './QuizArea.svelte';
</script>

<style>
  main {
    text-align: center;
    padding: 1em;
    max-width: 240px;
    margin: 0 auto;
  }

  h1 {
    text-transform: uppercase;
    font-size: 4em;
    font-weight: 100;
  }

  @media (min-width: 640px) {
    main {
      max-width: none;
    }
  }
</style>

<main>
  <!-- <QuizArea></QuizArea> -->
</main>
```

- As you can see in the code, we have the `script`, `style` and `markup` for the component. At this point, the code does nothing fancy other than just applying some styles to the app.
- But soon we'll uncomment the `QuizArea` component.

---

I hope that you have gotten a basic idea of the different parts in a Svelte file/component.

I have not added all the files from the project in this post but if you would like to reference the code at any moment it is available [here](https://github.com/manan30/svelte-trivia).

Now, let's create a new `QuizArea` component. For that create a file called `QuizArea.svelte` in the **src** directory.

We'll look at each of the three parts separately.

- First up we have the `<styles></styles>` tag. You can add any styles that you want for that component in between the `<style>` tag.
- Instead of writing `CSS` in a separate file, in Svelte we write the styles within the component itself.
- I have defined styles for the `QuizArea` component in the code below, but you can style it the way you want it.

```html
<style>
  #main {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translateX(-50%) translateY(-50%);
    height: calc(100vh - 40%);
    width: calc(100vw - 40%);
    padding: 15px;

    background-color: white;
    border-radius: 6px;
    box-shadow: 0 0 5px white;

    text-align: left;
  }

  span {
    display: block;
    margin-top: 20px;
  }

  button {
    margin-top: 15px;
    margin-right: 15px;
    padding: 10px;
    float: right;

    color: white;
    background-color: #ff3e00;
    border: none;
    border-radius: 10px;
    cursor: pointer;
  }

  button:hover {
    box-shadow: 0 0 5px #ff3e00;
  }

  #heading {
    font-size: 24px;
    font-weight: bolder;
  }

  #difficulty {
    position: absolute;
    right: 16px;
    top: 16px;
    height: 25px;
    width: 80px;
    padding: 5px;

    background: rgb(97, 225, 230);
    color: white;
    text-align: center;
    border-radius: 16px;
  }

  #category {
    font-size: 12px;
    font-weight: normal;
  }

  #button-bar {
    position: absolute;
    bottom: 16px;
    right: 0;
  }

  #choice {
    margin-top: 16px;
    padding: 8px;

    border: 1px solid #4e5656;
    border-radius: 8px;
  }

  #choice:hover {
    cursor: pointer;
    background: green;
    border: 1px solid green;
    color: white;
  }

  #snackbar {
    position: absolute;
    left: 16px;
    bottom: 16px;
  }
</style>
```

This was easy, nothing fancy or Svelte specific. The only thing is we write `styles` in the same file as the other component code.

- Next up we are going to talk about the `<script></script>` tag. We'll be writing all our JavaScript inside of this tag and here is where we'll be looking at how Svelte does things.
- So, in Svelte we'll be using `let` or `const` to declare variables. All the variables that we declare are essential `state` variables. And all the rules of `JavaScript` apply to these variables, so `const` variables cannot be reassigned while `let` variables can be reassigned.
- They are the same as the variables that we declare using `useState()` in React.
- The best part about Svelte is that the component automatically re-renders whenever the value of state variable changes. But there is no need to call any `set` function.

```js
// In Svelte
let name = 'Manan';

// Same thing in React
const [name, setName] = React.useState('Manan');

// causes component to re-render
name = 'Maitry';

// Same thing in React
setName('Maitry');
```

- We have talked about `state` so it's natural that we talk about `props`. In Svelte, you can declare a prop by just adding the `export` keyword behind the variable declaration.

```js
// props in Svelte
export let name;
```

- The `name` prop can now be used in other components. We can declare any number of props as we do it in React.
- We can even declare `functions` that can act as our `event handlers` or can serve any other purpose like fetching data, providing utility operations, etc.

```js
// on click handler
function handleClick(change) {
  snackbarVisibility = false;

  if (change === 'f') questionNo += 1;
  else questionNo -= 1;

  question = htmlDecode(data[questionNo].question);
  answerChoices = shuffle(
    [
      ...data[questionNo].incorrect_answers,
      data[questionNo].correct_answer
    ].map(a => htmlDecode(a))
  );
  answer = htmlDecode(data[questionNo].correct_answer);
  category = htmlDecode(data[questionNo].category);
  difficulty = data[questionNo].difficulty;
}
```

- We can import other modules or packages or components by using the `import` keyword. This is similar to what we do in React.

```js
// imports the Snackbar component
import Snackbar from './Snackbar.svelte';
```

The main takeaway from this part is that we can write whatever `JavaScipt` we want with a few twists and the compiler will do the rest for us.

Now the question is how can we use our JavaScript variables in the HTML markup. So for the final part of the app, we will look into that.

- It is pretty simple to render any variable. We just wrap the variable in curly braces like this `{variableName}`.

```html
<!-- see how simple it is :smiley:-->
<p>Hello {name}!</p>

<!-- outputs -->
Hello Manan
```

- Remember the markup inside a Svelte file is Html-ish and hence we can use built-in Svelte expressions to perform things like rendering something conditionally or looping over given values.
- To render something conditionally we use `{#if expression}<div></div> {/if}`. Here the `expression` can be any valid variable or expression that is in scope (i.e declared inside the `<script>` tag)

```html
{#if name}
<div id="snackbar">
  <Snackbar message="{correct}"></Snackbar>
</div>
{/if}
```

- To loop over an array we use the `{#each expression as exp}<div></div>{/each}`. Here the `expression` is an iterable value while `exp` is each entry of that iterable value.

```html
{#each answerChoices as choice}
<div id="choice" on:click="{(e) => handleAnswerChoice(e)}">
  <i>{choice}</i>
</div>
{/each}
```

This is just the tip of the iceberg and you can learn more about everything that Svelte can do over [here](https://svelte.dev/docs).

With this, we can now stitch our component together. Copy and paste the code given below in your `QuizArea.svelte` file

```html
<script>
  import { onMount } from 'svelte';
  import { htmlDecode, shuffle } from './utils.js';
  import Snackbar from './Snackbar.svelte';

  let data;

  let questionNo = 0;
  let question = 'loading...';
  let answerChoices;
  let answer;
  let category = 'loading...';
  let difficulty = 'loading...';

  let correct = false;
  let snackbarVisibility = false;
  $: score = 0;

  // function for fetching data
  function fetchData() {
    fetch('https://opentdb.com/api.php?amount=10')
      .then(resp => resp.json())
      .then(res => {
        data = res.results;
        question = htmlDecode(data[questionNo].question);
        answerChoices = shuffle(
          [
            ...data[questionNo].incorrect_answers,
            data[questionNo].correct_answer
          ].map(a => htmlDecode(a))
        );
        answer = htmlDecode(data[questionNo].correct_answer);
        category = htmlDecode(data[questionNo].category);
        difficulty = data[questionNo].difficulty;
      })
      .catch(e => console.error(e));
  }

  onMount(fetchData);

  // function for moving onto next/prev question
  function handleClick(change) {
    snackbarVisibility = false;

    if (change === 'f') questionNo += 1;
    else questionNo -= 1;

    question = htmlDecode(data[questionNo].question);
    answerChoices = shuffle(
      [
        ...data[questionNo].incorrect_answers,
        data[questionNo].correct_answer
      ].map(a => htmlDecode(a))
    );
    answer = htmlDecode(data[questionNo].correct_answer);
    category = htmlDecode(data[questionNo].category);
    difficulty = data[questionNo].difficulty;
  }

  // function to check the correctness of an answer
  function handleAnswerChoice(e) {
    if (e.target.innerText === answer && !correct) {
      correct = true;
      score += 1;
    } else if (correct) correct = false;
    snackbarVisibility = true;
  }
</script>

<style>
  #main {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translateX(-50%) translateY(-50%);
    height: calc(100vh - 40%);
    width: calc(100vw - 40%);
    padding: 15px;

    background-color: white;
    border-radius: 6px;
    box-shadow: 0 0 5px white;

    text-align: left;
  }

  span {
    display: block;
    margin-top: 20px;
  }

  button {
    margin-top: 15px;
    margin-right: 15px;
    padding: 10px;
    float: right;

    color: white;
    background-color: #ff3e00;
    border: none;
    border-radius: 10px;
    cursor: pointer;
  }

  button:hover {
    box-shadow: 0 0 5px #ff3e00;
  }

  #heading {
    font-size: 24px;
    font-weight: bolder;
  }

  #difficulty {
    position: absolute;
    right: 16px;
    top: 16px;
    height: 25px;
    width: 80px;
    padding: 5px;

    background: rgb(97, 225, 230);
    color: white;
    text-align: center;
    border-radius: 16px;
  }

  #category {
    font-size: 12px;
    font-weight: normal;
  }

  #button-bar {
    position: absolute;
    bottom: 16px;
    right: 0;
  }

  #choice {
    margin-top: 16px;
    padding: 8px;

    border: 1px solid #4e5656;
    border-radius: 8px;
  }

  #choice:hover {
    cursor: pointer;
    background: green;
    border: 1px solid green;
    color: white;
  }

  #snackbar {
    position: absolute;
    left: 16px;
    bottom: 16px;
  }

  @media screen and (max-width: 960px) {
    #main {
      width: calc(100vw - 15%);
    }
    #difficulty {
      top: -16px;
    }
  }
</style>

<div id="main">
  <span id="heading"
    >Question {questionNo + 1}
    <i id="category">(Category - {category})</i></span
  >
  <span>{question}</span>
  <div id="difficulty">{difficulty}</div>

  {#if answerChoices} {#each answerChoices as choice}
  <div id="choice" on:click="{(e) => handleAnswerChoice(e)}">
    <i>{choice}</i>
  </div>
  {/each} {/if}

  <div id="button-bar">
    {#if !(questionNo > 10)}
    <button value="Next" on:click="{() => handleClick('f')}">Next</button>
    {/if} {#if questionNo > 0}
    <button value="Back" on:click="{() => handleClick('b')}">
      Previous
    </button>
    {/if}
  </div>

  {#if snackbarVisibility}
  <div id="snackbar">
    <Snackbar message="{correct}"></Snackbar>
  </div>
  {/if}
</div>
```

And there we have it an app written completely in Svelte. Go ahead and use `npm run dev` to see your app in action. This is a really small app that demonstrates what we can do with Svelte and for me, this might revolutionize the way we design for the web and I am very much excited about what's ahead in store for us.

The main goal of this article was to give you an overview of Svelte and how awesome it is. I hope you feel a little bit more comfortable about using Svelte now.

---

## Thoughts 💭

Please let me know your thoughts about Svelte in the discussion section below. Also, don't hesitate to ask any questions if you are stuck somewhere in the app or would like to learn more about it or are confused about any part.

---

Thank you for reading!

As always, connect with me on [Twitter](https://twitter.com/Manan_30) and [Instagram](https://www.instagram.com/manan__joshi/).

Until next time, peace out and happy coding !!!

Cheers.