Making a TODO App in Svelte
Tags: javascript, svelte, tutorial
Published: 2020-02-28 18:02:41

Rumor has it that a new JS framework is created every 6 minutes. No surprise then that only a few of them come out on top, with the most recent addition to this group being  [Svelte](https://svelte.dev/).

So, in this tutorial, I’d like to guide you through your first steps into the world of Svelte by making a simple  **TODO app**. Hope you'll enjoy it!

# Setup
Time is money and when setting up a playground for anything you’d like to only experiment with, you want to do this as fast as possible. Thus, for this tutorial, I recommend you use  **CodeSandbox**  with its  [Svelte template](https://codesandbox.io/s/svelte-svelte)  or one of the official  **Svelte boilerplates**  available on  [GitHub](https://github.com/sveltejs), e.g.:

```bash
npx degit sveltejs/template svelte-todo
cd svelte-todo
npm install
```

Otherwise, if you’ve got some time to spend, you can configure pretty much any of the most popular JS bundlers ([Rollup](https://github.com/sveltejs/rollup-plugin-svelte),  [Webpack](https://github.com/sveltejs/svelte-loader),  [Parcel](https://github.com/DeMoorJasper/parcel-plugin-svelte)) with an additional Svelte  **plugin**/**loader**. You can check out the list of pretty much all Svelte integrations in  [this repo](https://github.com/sveltejs/integrations).

## Code
Anyway, no matter what setup method you've used, all our work will be happening within a single  `App.svelte`  file.

Let's do a few preparations first:

```html
<script></script>

<svelte:head>
  <link
    rel="stylesheet"
    type="text/css"
    href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css"
  />
  <script src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
</svelte:head>

<main class="container is-fluid">
  <div class="columns is-centered is-vcentered is-mobile">
    <div class="column is-narrow" style="width: 70%">
      <h1 class="has-text-centered title">Svelte TODO</h1>
      <form></form>
      <ul></ul>
    </div>
  </div>
</main>
```

Our code consists of 3 main sections:

-   `<script>`  - a block containing all the required JavaScript code.
-   `<svelte:head>`  - a special Svelte-provided block that allows us to insert elements into the  `<head>`  tag, without leaving the comfort zone of our Svelte component.
-   `<main>`  - a block which is part of our app's  **template**.

Now, I'm using the  `<svelte:head>`  element to import the  [Bulma CSS framework](http://bulma.io/)  and  [Font Awesome](https://fontawesome.com/)  library from external CDNs. The first will be used to make our app look more stylish, while the second gives us access to all the free  [Font Awesome icons](http://fontawesome.com/icons)  out there. You can also import these resources using  **NPM**  or other methods (which are listed on the respective projects' websites), but CDN is certainly the simplest and thus the best for our use.

There's one thing to note about our use of a  **CSS framework**. As Svelte is a somewhat young framework, and doesn't have its own "established" component library, like Vue or React do, CSS frameworks are the easiest ways to make a Svelte app look good without having to write all the code on your own. That's why we're using Bulma here, and why I recommend you also use this or other CSS frameworks in your own Svelte apps.

# JavaScript
With the fundamentals of our app in place, we can start writing the actual JS code. Surprisingly enough, there's not much of it!

```javascript
let todos = [];
let input = "";

function addTodo() {
  if (input) {
    todos = [
      ...todos,
      {
        text: input,
        id: Math.random()
            .toString(36)
            .substr(2, 9)
      }
    ];
  }
  input = "";
}

function removeTodo(id) {
  const index = todos.findIndex(todo => todo.id === id);
  todos.splice(index, 1);
  todos = todos;
}
```

Because Svelte is a  [compiler](https://areknawo.com/svelte-a-recipe-for-a-successful-framework/), its  **reactivity system**  can be based on simple  **local variables**. In our case, there're only 2 of those:

-   `todos`  which stores all out TODO objects.
-   `input`  which stores the user input.

In addition to these 2 variables, we've got 2 simple functions -  `addTodo()`  and  `removeTodo()`, both doing exactly what their names imply.

One important detail to note here is the weird  `todos = todos`  assignment in the  `removeTodo()`  function. In vanilla JS such operation would be pointless, but here, Svelte relies on such assignments to detect whether it should update the view or not. Svelte doesn't recognize methods like  `splice()`  (which we use to remove a TODO from the array) or  `push()`  - only variable and property assignments. That's why we have to do the  `todos = todos`  operation - to trigger the  **view update**.

On the other hand, in addTodo() function, we could use push() and do the same assignment trick as we do in  `removeTodo()`, but we use a much nicer and cleaner  [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax)  instead, where an assignment is simply required.

# The form
With the JS code ready, we can proceed to make the actual template. Let's start with a form that lets the user add new TODOs:

```html
<form
  class="field has-addons"
  style="justify-content: center"
  on:submit|preventDefault="{addTodo}"
>
  <div class="control">
    <input bind:value="{input}" class="input" type="text" placeholder="TODO" />
  </div>
  <div class="control">
    <button class="button is-primary">
      <span class="icon is-small">
        <i class="fas fa-plus"></i>
      </span>
    </button>
  </div>
</form>
```

We won't be focusing on all the CSS classes in here as they're all quite self-explanatory and taken straight from the  [Bulma CSS framework](https://bulma.io/documentation). Instead, let's zoom in on all the Svelte features used in the snippet!

On the very upper  `<form>`  element, we use the  `on:`  directive to listen to the form's  `submit`  event. The  `|[modifier]`  syntax allows us to apply  **modifiers**  to the listener, like  `preventDefault`, in order to prevent the page from reloading on form submission. Next, we use the  `bind:`  directive with  `<input>`  element to bind the element's value property to the  `input`  variable. It's all we have to do, as Svelte will take care of the rest on its own.

Currently, our app should look like this:

![Svelte TODO form](https://areknawo.com/content/images/2020/02/second-step.png)

# TODO list
Right now, there's not much left to do other than the TODO list itself. Thankfully, with Svelte it's quite easy!

```html
<ul class:list={todos.length > 0}>
	{#each todos as todo (todo.id)}
		<li class="list-item" transition:slide="{{duration: 300, easing: elasticInOut}}">
			<div class="is-flex" style="align-items: center">
				<span class="is-pulled-left">{todo.text}</span>
				<div style="flex: 1"></div>
				<button class="button is-text is-pulled-right is-small" on:click={()=> removeTodo(todo.id)}>
					<span class="icon">
						<i class="fas fa-check"></i>
					</span>
				</button>
			</div>
		</li>
	{:else}
		<li class="has-text-centered" transition:slide="{{delay: 600, duration: 300, easing: elasticInOut}}">
			Nothing here!
		</li>
	{/each}
</ul>
```

First, we add the  `class:`  directive to our upper  `<ul>`  element. This is required to trigger a certain class name - in this case,  `list`  - when a provided condition is met. We use it as we don't want to apply the  `list`  class when there are no TODOs to be shown. This is necessary as we want to esthetically show the  _"Nothing here!"_  message.

Next, we see our first and the only used  **Svelte block**  -  `{#each}`. It iterates over an array of values (here it's the  `todos`  array) and renders the provided template while passing the data object under the specified name (`todo`). The last thing here is the **key expression** (`(todo.id)`) which helps Svelte optimize the list rendering process by assigning the specified key to each item.

We also make a use of the  `{:else}`  clause, which will be rendered when the length of the passed array is equal to  `0`. It's the ideal place for our "Nothing here!" message.

Each item rendered with the use of  `{#each}`  block has access to the item's data through the  `todo`  object. We take advantage of that when displaying the TODO text with  `{todo.text}`  and listening to the click event with  `on:`  directive and  **inline handler**.

Lastly, to make our app slightly more appealing, we use Svelte's built-in  `slide`  transition, to smoothly animate all the TODO items and the "Nothing here!" message.

To do this we first have to import the necessary  **transition**  and  **easing functions**  from the Svelte's library, at the top of our JS code:

```javascript
import { slide } from "svelte/transition";
import { elasticInOut } from "svelte/easing";
```

Then, we can use them within our template through the  `transition:`  directive. It takes a transition function (either Svelte's built-in or your own) and a  **config object**, which varies depending on the transition. In our case, the config allows for setting values like animation delay, duration and the easing function (`elasticInOut`).

# The results
With all set and done, here's our TODO app in its full glory:
{% codesandbox svelte-todo-app-qtw8u %}
Feel free to play with it on your own - either through the provided playground or by following the tutorial on your own.

# Final thoughts
Overall, I'm really surprised by how easy and comfortable it is to work with Svelte. The advantages of having a compiler are not only visible on the client side (like the improved  **performance**  and  **smaller bundles**), but also from the developer perspective with features like local variables-based reactivity and more. Apart from the small (but growing) community and ecosystem of libraries and tools, Svelte really is a  **viable option**  to consider for your next web app!

If you like the post consider  **sharing it**  and  **following me**  on  [Twitter](https://twitter.com/areknawo), [Facebook](http://facebook.com/areknawoblog), or visit [my personal blog](https://areknawo.com). If you're interested in my content, I also recommend checking out my [YouTube channel](https://www.youtube.com/channel/UCUw6bg379ONlG1E_oxhwi-A/). Again, thanks for reading this piece and have a nice day!