DEV Community

Cover image for How to Build a Todo app with Svelte!
Avneesh Agarwal
Avneesh Agarwal

Posted on • Originally published at blog.avneesh.tech

How to Build a Todo app with Svelte!

Demo

Video demo

Setup

Create app

npx degit sveltejs/template svelte-todo-app
Enter fullscreen mode Exit fullscreen mode

cd into the folder-

cd svelte-todo-app
Enter fullscreen mode Exit fullscreen mode

Install dependencies

npm install # npm

yarn install # yarn
Enter fullscreen mode Exit fullscreen mode

Start app

npm run dev # npm

yarn dev # yarn
Enter fullscreen mode Exit fullscreen mode

Cleanup

I don't want the default stylings, so I will replace the stylings in globals.css with this-

* {
  margin: 0;
}

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
    "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
    monospace;
}
Enter fullscreen mode Exit fullscreen mode

Building the container

In App. svelte let's create the starter code for our app.

<script>
</script>

<main class="container">
</main>

<style>
  .container {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-height: 90vh;
    background: #222e50
      url(https://images.unsplash.com/photo-1579546929518-9e396f3cc809?ixid=MXwxMjA3fDB8MHxzZWFyY2h8N3x8Z3JhZGllbnR8ZW58MHx8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60)
      no-repeat;
    background-size: cover;
    padding-top: 10vh;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

Building the form to submit Todos

Inside our main container, add a form component with input and button-

<main class="container">
  <div>
    <form on:submit|preventDefault={addTodo}>
      <input
        bind:value={newItem}
        type="task"
        class="todos__input"
        placeholder="Enter Todo"
      />
      <button class="todos__button">+</button>
    </form>
  </div>
</main>
Enter fullscreen mode Exit fullscreen mode

We need to create the following things now-

  • A variable to store the input value

  • A addTodo function for adding the todos.

So in the script tag add the following-

  let newItem = "";
  let todoList = [];

  function addTodo() {
    if (newItem !== "") {
      todoList = [
        ...todoList,
        {
          task: newItem,
          completed: false,
        },
      ];
      newItem = "";
    }

    console.log(todoList);
  }
Enter fullscreen mode Exit fullscreen mode

Styling

Now let's style our submit button and the input. Inside the styles, tag add this-

.todos__input {
    background-color: inherit;
    border: none;
    box-shadow: none;
    text-decoration: none;
    font-size: 1.2rem;
    border-bottom: 1px solid black;
    margin-top: 15px;
    outline: none;
    width: 500px;
  }
  .todos__button {
    background-color: inherit;
    border: none;
    box-shadow: none;
    font-size: 1.2rem;
    cursor: pointer;
  }
Enter fullscreen mode Exit fullscreen mode

If we now add an item, it will add the item to the list and console log it.

Rendering the todos

In React like we have a map function, we do it through #each in Svelte

 {#each todoList as item, index}
      <div class="todo">
        <span class="todo__text">{item.task}</span>
      </div>
    {/each}
Enter fullscreen mode Exit fullscreen mode

Styling the todos

 .todo {
    display: flex;
    padding: 20px;
    border-radius: 20px;
    box-shadow: 0 0 15px rgb(0 0 0 / 20%);
    background-color: hsla(0, 0%, 100%, 0.2);
    -webkit-backdrop-filter: blur(25px);
    backdrop-filter: blur(25px);
    width: inherit;
    margin-top: 15px;
    font-size: 1.2rem;
    justify-content: space-between;
    align-items: center;
  }
Enter fullscreen mode Exit fullscreen mode

This will give the todos a glassmorphic look 🤩

image.png

Adding a header for the todos

The to-do list and the inputs look kinda clumsy, so let's add a header there-

After the form component add this h2 tag-

<h2 class="todos__listHeader">Todos</h2>
Enter fullscreen mode Exit fullscreen mode

The stylings for this header-

 .todos__listHeader {
    text-align: center;
    padding: 20px;
    border-radius: 20px;
    box-shadow: 0 0 15px rgb(0 0 0 / 20%);
    background-color: hsla(0, 0%, 100%, 0.2);
    -webkit-backdrop-filter: blur(25px);
    backdrop-filter: blur(25px);
    margin: 15px 0px 25px 0px;
    font-size: 1.2rem;
  }
Enter fullscreen mode Exit fullscreen mode

Now we have got a pretty good separation there.

image.png

Creating the complete and delete functionality

We are going to use icons for delete and complete, so let's get the icons first.

  • Create Icons. svelte file in the src directory. Add the following piece of code, for the icons. Feel free to change the icons :) -
<script>
  export let name;
  export let width = "1.5rem";
  export let height = "1.5rem";
  export let focusable = false;
  let icons = [
    {
      box: 24,
      name: "check-mark",
      svg: `<svg focusable="false" viewBox="0 0 24 24" aria-hidden="true"><path d="M19.77 4.93l1.4 1.4L8.43 19.07l-5.6-5.6 1.4-1.4 4.2 4.2L19.77 4.93m0-2.83L8.43 13.44l-4.2-4.2L0 13.47l8.43 8.43L24 6.33 19.77 2.1z"></path></svg>`,
    },
    {
      box: 32,
      name: "delete",
      svg: `<svg focusable="false" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM8 9h8v10H8V9zm7.5-5l-1-1h-5l-1 1H5v2h14V4h-3.5z"></path></svg>`,
    },
  ];
  let displayIcon = icons.find((e) => e.name === name);
</script>

<svg
  class={$$props.class}
  {focusable}
  {width}
  {height}
  viewBox="0 0 {displayIcon.box} {displayIcon.box}">{@html displayIcon.svg}</svg
>
Enter fullscreen mode Exit fullscreen mode

Show the icons

Inside the todo, I will add a div with two buttons that have the icons as follows-

{#each todoList as item, index}
  <div class="todo">
    <span class="todo__text">{item.task}</span>

    <div class="icons">
      <button
        class="icon__button"
        on:click={() => (item.completed = !item.completed)}
      >
        <Icons name="check-mark" class="icon" />
      </button>

      <button class="icon__button" on:click={() => removeFromList(index)}>
        <Icons name="delete" class="icon" />
      </button>
    </div>
  </div>
{/each}
Enter fullscreen mode Exit fullscreen mode

Import the icons like this-

import Icons from "./Icons.svelte";
Enter fullscreen mode Exit fullscreen mode

Creating the delete function-

 function removeFromList(index) {
    todoList.splice(index, 1);
    todoList = todoList;
  }
Enter fullscreen mode Exit fullscreen mode

Styling the buttons

Add the following styles to get a beautiful icon button-

 .icon__button {
    background-color: transparent;
    border: none;
    box-shadow: none;
    font-size: 1.2rem;
    cursor: pointer;
    color: rgba(0, 0, 0, 0.54);
  }
  .icon {
    background: rgba(0, 0, 0, 0.54);
  }
Enter fullscreen mode Exit fullscreen mode

Striking the Text

Add this optional class to the item. task span, so if the item is completed then it will add the class-

 <span
          class={`todo__text ${item.completed ? "todo__checked--strike" : ""}`}
          >{item.task}</span
        >
Enter fullscreen mode Exit fullscreen mode

Now, we need to add the styles to strike it-

  .todo__checked--strike {
    text-decoration: line-through;
  }
Enter fullscreen mode Exit fullscreen mode

This function takes an argument of index and splices the todoList to remove the item.

We have successfully built a to-do app in Svelte! 🥳🎉

To extend your knowledge about svelte check out this video by James Q Quick where he will show you how to create a todo app with Sveltekit and tailwind CSS!

%[https://youtu.be/]

Useful links

Github Repository

Demo

James' channel

Connect with Me

Top comments (7)

Collapse
 
cjsmocjsmo profile image
Charlie J Smotherman

Are your todo' lost when you close the app or do they persist until you open the app again?

Collapse
 
avneesh0612 profile image
Avneesh Agarwal

Yeah, I haven't implemented localstorage in this one :)

Collapse
 
cjsmocjsmo profile image
Charlie J Smotherman

If I remember correctly (and I dont always do) the stock demo app you get when you

npm init svelte@next

Has a persistent todo list example you may want to take some inspiration from.

Looks good other wise

Happy coding

Thread Thread
 
avneesh0612 profile image
Avneesh Agarwal

Um, I didn't know you can generate like that and I know how to add that feature but wanted to keep this tutorial simple. Thanks for understanding :)

Thread Thread
 
cjsmocjsmo profile image
Charlie J Smotherman

👍

Collapse
 
zippcodder profile image
Deon Rich

Nice article!

Collapse
 
avneesh0612 profile image
Avneesh Agarwal

Thanks!