Demo
Setup
Create app
npx degit sveltejs/template svelte-todo-app
cd into the folder-
cd svelte-todo-app
Install dependencies
npm install # npm
yarn install # yarn
Start app
npm run dev # npm
yarn dev # yarn
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;
}
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>
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>
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);
}
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;
}
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}
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;
}
This will give the todos a glassmorphic look 🤩
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>
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;
}
Now we have got a pretty good separation there.
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
>
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}
Import the icons like this-
import Icons from "./Icons.svelte";
Creating the delete function-
function removeFromList(index) {
todoList.splice(index, 1);
todoList = todoList;
}
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);
}
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
>
Now, we need to add the styles to strike it-
.todo__checked--strike {
text-decoration: line-through;
}
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!
Oldest comments (7)
Nice article!
Thanks!
Are your todo' lost when you close the app or do they persist until you open the app again?
Yeah, I haven't implemented localstorage in this one :)
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
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 :)
👍