DEV Community

Cover image for Simple To-Do List Application in Vue.js 3: Step-by-Step Guide 2025
Chabba Saad
Chabba Saad

Posted on • Edited on

12

Simple To-Do List Application in Vue.js 3: Step-by-Step Guide 2025

This guide will walk you through building a basic To-Do List application using Vue.js 3. By the end of this tutorial, you'll understand how to handle data binding, event handling, and dynamic rendering in Vue.js.

Prerequisites

Basic knowledge of HTML, CSS, and JavaScript.
Node.js and npm installed on your computer.
Vue CLI installed. If not, you can install it by running npm install -g @vue/cli.

Step 1: Setting Up the Project

Create a New Vue.js Project: Open your terminal and run the following command to create a new Vue.js 3 project:



vue create todo-app


Enter fullscreen mode Exit fullscreen mode

Navigate into the project directory:



cd todo-app


Enter fullscreen mode Exit fullscreen mode

Run the Development Server: Start the Vue development server:



npm run serve


Enter fullscreen mode Exit fullscreen mode

This will open the default Vue.js starter template in your browser at http://localhost:8080.

Step 2: Structuring the Application

Clean Up the Default Template: Open src/App.vue and remove the default template, script, and style contents. Replace them with the following code:



<template>
  <div id="app">
    <h1>Vue.js To-Do List</h1>
    <input v-model="newTask" placeholder="Add a new task" @keyup.enter="addTask" />
    <button @click="addTask">Add Task</button>
    <ul>
      <li v-for="(task, index) in tasks" :key="index">
        <input type="checkbox" v-model="task.completed" />
        <span :class="{ 'completed-task': task.completed }">{{ task.text }}</span>
        <button @click="removeTask(index)">Delete</button>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      newTask: '',
      tasks: []
    };
  },
  methods: {
    addTask() {
      if (this.newTask.trim() !== '') {
        this.tasks.push({ text: this.newTask, completed: false });
        this.newTask = '';
      }
    },
    removeTask(index) {
      this.tasks.splice(index, 1);
    }
  }
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.completed-task {
  text-decoration: line-through;
  color: grey;
}
input {
  margin-bottom: 10px;
  padding: 5px;
}
button {
  margin-left: 5px;
  padding: 5px;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 5px 0;
}
</style>



Enter fullscreen mode Exit fullscreen mode

Step 3: Understanding the Code

Template Section:

<input v-model="newTask" placeholder="Add a new task" @keyup.enter="addTask" />
: This input field is bound to the newTask data property using v-model, enabling two-way data binding. The @keyup.enter="addTask" listens for the Enter key to trigger the addTask method.

<button @click="addTask">Add Task</button>
: This button triggers the addTask method when clicked.

<ul>: This unordered list uses v-for to loop through the tasks array and render each task. The :key="index" is a unique identifier for each item, required by Vue to track list items efficiently.

<input type="checkbox" v-model="task.completed" />: A checkbox that toggles the completed status of a task.

<span :class="{ 'completed-task': task.completed }">{{ task.text }}</span> : The span element displays the task text. The :class directive conditionally applies the completed-task CSS class based on whether the task is completed.

<button @click="removeTask(index)">Delete</button> : A button to remove the task from the list

Script Section:

data() Method:
newTask: A string that holds the value of the new task to be added.
tasks: An array that holds all tasks, each represented as an object with text and completed properties.

methods Object:
addTask(): This method adds the newTask to the tasks array and then clears the newTask input field.
removeTask(index): This method removes a task from the tasks array based on its index.

Style Section:

Basic CSS to style the to-do list, with a .completed-task class to strike through completed tasks.

Persist Tasks in Local Storage: You can enhance the app by saving tasks in the browser's local storage, so they persist across page reloads.



mounted() {
  this.tasks = JSON.parse(localStorage.getItem('tasks')) || [];
},
methods: {
  addTask() {
    if (this.newTask.trim() !== '') {
      this.tasks.push({ text: this.newTask, completed: false });
      this.newTask = '';
      localStorage.setItem('tasks', JSON.stringify(this.tasks));
    }
  },
  removeTask(index) {
    this.tasks.splice(index, 1);
    localStorage.setItem('tasks', JSON.stringify(this.tasks));
  }
}


Enter fullscreen mode Exit fullscreen mode

Editing Tasks:



<template>
  <div id="app">
    <h1>Vue.js To-Do List</h1>
    <input v-model="newTask" placeholder="Add a new task" @keyup.enter="addTask" />
    <button @click="addTask">Add Task</button>
    <ul>
      <li v-for="(task, index) in tasks" :key="index">
        <input type="checkbox" v-model="task.completed" />

        <span v-if="!task.isEditing" :class="{ 'completed-task': task.completed }">{{ task.text }}</span>
        <input v-else v-model="task.text" @keyup.enter="saveTask(task)" @blur="saveTask(task)" />

        <button v-if="!task.isEditing" @click="editTask(task)">Edit</button>
        <button @click="removeTask(index)">Delete</button>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      newTask: '',
      tasks: []
    };
  },
  methods: {
    addTask() {
      if (this.newTask.trim() !== '') {
        this.tasks.push({ text: this.newTask, completed: false, isEditing: false });
        this.newTask = '';
      }
    },
    removeTask(index) {
      this.tasks.splice(index, 1);
    },
    editTask(task) {
      task.isEditing = true;
    },
    saveTask(task) {
      task.isEditing = false;
    }
  }
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.completed-task {
  text-decoration: line-through;
  color: grey;
}
input {
  margin-bottom: 10px;
  padding: 5px;
}
button {
  margin-left: 5px;
  padding: 5px;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 5px 0;
}
</style>



Enter fullscreen mode Exit fullscreen mode

Install Font Awesome

First, include Font Awesome in your project. If you're using a CDN, you can add this link in your HTML file or directly in the

section of your index.html.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">

** Update the Template with Icons
**

Now, let's add some Font Awesome icons for editing, deleting, and completing tasks.



<template>
  <div id="app" class="container">
    <h1>Vue.js To-Do List</h1>
    <div class="input-container">
      <input v-model="newTask" placeholder="Add a new task" @keyup.enter="addTask" />
      <button @click="addTask" class="btn btn-add"><i class="fas fa-plus"></i></button>
    </div>
    <ul>
      <li v-for="(task, index) in tasks" :key="index" :class="{ completed: task.completed }">
        <input type="checkbox" v-model="task.completed" id="checkbox" />

        <span v-if="!task.isEditing" class="task-text">{{ task.text }}</span>
        <input v-else v-model="task.text" @keyup.enter="saveTask(task)" @blur="saveTask(task)" class="edit-input" />

        <div class="actions">
          <button v-if="!task.isEditing" @click="editTask(task)" class="btn btn-edit"><i class="fas fa-edit"></i></button>
          <button @click="removeTask(index)" class="btn btn-delete"><i class="fas fa-trash-alt"></i></button>
        </div>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      newTask: '',
      tasks: []
    };
  },
  methods: {
    addTask() {
      if (this.newTask.trim() !== '') {
        this.tasks.push({ text: this.newTask, completed: false, isEditing: false });
        this.newTask = '';
      }
    },
    removeTask(index) {
      this.tasks.splice(index, 1);
    },
    editTask(task) {
      task.isEditing = true;
    },
    saveTask(task) {
      task.isEditing = false;
    }
  }
};
</script>

<style>
body {
  background-color: #f4f7f6;
  font-family: 'Roboto', sans-serif;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

.container {
  background-color: #ffffff;
  border-radius: 10px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
  padding: 20px;
  max-width: 400px;
  width: 100%;
}

h1 {
  color: #333;
  margin-bottom: 20px;
}

.input-container {
  display: flex;
  margin-bottom: 20px;
}

input {
  flex: 1;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  font-size: 16px;
}

.btn {
  background-color: #4caf50;
  color: white;
  border: none;
  padding: 10px;
  border-radius: 5px;
  cursor: pointer;
  margin-left: 5px;
  transition: background-color 0.3s;
}

.btn:hover {
  background-color: #45a049;
}

.btn-add {
  background-color: #28a745;
}

.btn-edit {
  background-color: #ffc107;
}

.btn-delete {
  background-color: #dc3545;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
  padding: 10px;
  border-radius: 5px;
  background-color: #f8f9fa;
}

li.completed .task-text {
  text-decoration: line-through;
  color: #888;
}

li:hover {
  background-color: #e9ecef;
}

.actions {
  display: flex;
}

.actions .btn {
  margin-left: 5px;
}

.edit-input {
  flex: 1;
  margin-right: 10px;
}
</style>



Enter fullscreen mode Exit fullscreen mode

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

SurveyJS custom survey software

Simplify data collection in your JS app with a fully integrated form management platform. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more. Integrates with any backend system, giving you full control over your data and no user limits.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay