loading...
Cover image for I created a Quiz app using Svelte and now I cannot go back to any other framework.

I created a Quiz app using Svelte and now I cannot go back to any other framework.

manan30 profile image Manan Joshi Updated on ・9 min read

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 and the live demo.

I was hearing about Svelte a lot and after listening to this talk 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 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.

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

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

package.json

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.

<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.

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.
<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.
// 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.
// 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.
// 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.
// 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}.
<!-- 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)
{#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.
{#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.

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

<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 and Instagram.

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

Cheers.

Posted on by:

manan30 profile

Manan Joshi

@manan30

Passionate web and mobile developer, graduate student, pragmatist, love to cook, and sometimes wondering are we alone in the universe?

Discussion

markdown guide
 

Svelte or Nim for Frontend. ❤️

 
 
 

Yes Nim lang nim-lang.org
Svelte is great, but Svelte cant do Backend, Nim can do Backend, and Frontend.
:)

Wow, yeah nim is a good language. Good to hear someone using it on production server. Nice.

For Backend you can use Sapper... It's build by the Svelte team and is just as simple, fast and as lightweight.

No no nim-lang is another new language, like a scripting language. You can check that out on their website, its like C++ replacement. Not a frontend framework

Nim can do Frontend, thats what I use.

Going to check Sapper now, thats interesting.

Oich I though you use nim for your API too, I'm sorry

 

Great post thanks for sharing.

I don't get this part tho:

"making it orders of magnitude faster than frameworks or libraries that ship with a virtual DOM implementation"

Wasn't the whole premise of VDOM to make things faster since DOM is slow?

 

Long story short: Diffing isn't free


Explanation

The main reason we have been using things like VDOM is to compare if the data has changed or not and if so when to re-render the new changes. There are many diffing algorithms that are fast and we would update something if it has changed, but updating things directly will always be faster. In libraries like React the virtual DOM tries to compare its state with the current DOM state and if there is any difference it will ask the reconciler to apply those changes to the DOM. The part where it gets slow is the reconciliation phase. This is because diffing isn't free. It is nothing but just pure overhead. For more info, this is what the react docs have to say about virtual dom and this is what Svelte has to say about it.

 

Thank you for the answer, the article on the Svelte website makes total sense. I guess I need to learn more about how Svelte is updating the DOM without any diffing algo.

 

I like they way Svelte approaches the web. This is more align with the lean web thinking.

 
 

It is so difficult to fight the industry. Nowadays we think that every problem is a nail and we want to use the Ract/Vue hammer to do the job.

 

Having script, style and
Html all scoped in the same component seems to be similar to Vue! Not used Vue a lot, but is the usage expressions also similar? Eg:iterable with #each

 

I also haven't used Vue a lot but the one major difference between Svelte and Vue is that Svelte is a compiler and not a framework.

 

I think Vue3 is gonna have a similar way of working in terms of component file

 

Yes true. At the end of the day it's just semantics.

 

It wont compile, it asks for a utils.js file

 

As I wanted to keep this article small all files haven't been included.

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.

 
Sloan, the sloth mascot Comment marked as low quality/non-constructive by the community View code of conduct

I think you missed the point here, your title strongly indicates that Svelte is better for your quiz app and yet, I can't find that point in the whole content. Is it better because it is faster? I can't imagine React can't render your Quiz app fast enough. Or Svelte code is more concise and pretty? From your simple app, I can see that the code is very lengthy and ugly... what is the point? Sorry, I am just confused

 

The point that I am trying to make here is not bashing any other frameworks, but talking about how Svelte offers things from a different perspective as compared to other JS frameworks. You've to know the internals of different frameworks before making this comparison.

 
Sloan, the sloth mascot Comment marked as low quality/non-constructive by the community View code of conduct

You mean you are not saying Svelte is better, just taking a look? Your title clearly indicate that it is better than others, right? Isn't it misleading?

Do you anywhere see the word better or comparison with other frameworks anywhere in the article? The views are my own and I am not forcing anyone to believe in it. This is just my observation.

Sloan, the sloth mascot Comment marked as low quality/non-constructive by the community View code of conduct

Man. It is in the TITLE itself, you wrote "I created a Quiz app using Svelte and now I cannot go back to any other framework", right? Or someone else wrote that line for you? Didn't that mean you used some other framework before (React) and after you use Svelte for your quiz app, you find it so much better that you cannot go back to the other framework? Man, this is too much...

Hey look I don't care what you think. These are my views and it will stay that way. No one asked you to read it if you didn't like it. The way you're commenting is not the way to approach any of the articles here.

Sloan, the sloth mascot Comment marked as low quality/non-constructive by the community View code of conduct

It is very impolite and irresponsible to say that man. I am only saying that your title is misleading, and since you asked why, I explained it. Are you angry because I stated a fact that discomfort you? Am I insulting you or simply pointing out a mistake in your work? Why are you so emotional? Is it how you live your life?