Todo Example

Building a feature-rich Todo List application is the ultimate rite of passage after mastering the core concepts of Vue 3. This classic project goes far beyond a simple exercise; it provides a hands-on playground that covers essential concepts like data binding, event handling, and list rendering, while highlighting the distinct advantages of the Vue 3 Composition API in logic reuse and state management. By walking through this implementation, developers can gain a solid understanding of how to architect a reactive, highly maintainable frontend application from scratch.
At the heart of any Todo application lies the robust management of the task list state. In Vue 3, developers typically leverage ref or reactive to establish reactive data sources. Unlike the traditional Options API found in Vue 2—where related logic is fragmented across data, methods, and computed options—the Composition API allows us to co-locate related functionality cleanly.

Typically, the application maintains a reactive array of task objects. Each object contains a unique identifier (id), the task description (text), and a completion flag (completed). By wrapping this array in a ref, Vue's reactive engine automatically tracks its dependencies. When data shifts, the user interface updates instantaneously without any manual DOM manipulation.

Implementing Full CRUD Operations

Building the core functionality requires four primary operations: adding, deleting, toggling, and editing tasks.

  • Adding Tasks: Bind the input field to a reactive variable using event listeners. When a user presses the Enter key or clicks the submit button, a validation check ensures the input isn't empty. A valid entry instantiates a new task object, which is then pushed into the task array. Utilizing Date.now() or a UUID generator ensures unique IDs, preventing key collision bugs during rendering.
  • Deleting Tasks: Iterate through the task list and attach a deletion trigger to each item. Upon invocation, the delete handler filters out the target task by its ID using the filter method or modifies the array in place. Thanks to Vue 3's deep reactivity, these array mutations are reflected on screen immediately.
  • Toggling Status: Bind a checkbox element's checked state and listen for change events. Flipping the completed boolean property inside the handler updates the task status. Enhancing the user experience often involves applying conditional CSS rules to strike through completed tasks.
  • Editing Tasks: Editing usually involves swapping a text label for an active input field. This requires tracking the ID of the task currently under modification. When editing is triggered, the app saves the target ID, and upon blur or an Enter keystroke, commits the updated text and resets the editing state.

List Rendering and Performance Optimization

Templates render the task collection efficiently using the v-for directive. A critical best practice here is binding a unique :key attribute—usually the task's id—to every iterated node. Proper key assignments empower Vue's virtual DOM reconciliation algorithm to reuse existing nodes effectively and dodge redundant re-renders.

Furthermore, developers can harness computed properties (computed) to derive data dynamically, such as counting active versus finished tasks. Computed properties feature built-in caching mechanisms, re-evaluating only when their reactive dependencies change, which keeps application performance snappy.

Code Structure Example

Below is a simplified structural pattern utilizing the Composition API:

import { ref, computed } from 'vue'

export function useTodo() {
  const todos = ref([])
  const newTodoText = ref('')

  const remainingCount = computed(() => 
    todos.value.filter(todo => !todo.completed).length
  )

  function addTodo() {
    if (!newTodoText.value.trim()) return
    todos.value.push({
      id: Date.now(),
      text: newTodoText.value,
      completed: false
    })
    newTodoText.value = ''
  }

  function removeTodo(id) {
    todos.value = todos.value.filter(todo => todo.id !== id)
  }

  function toggleTodo(todo) {
    todo.completed = !todo.completed
  }

  return { todos, newTodoText, remainingCount, addTodo, removeTodo, toggleTodo }
}

Conclusion and Future Extensions

Building a Todo example offers developers deep insight into Vue 3's reactivity model and the architectural benefits of the Composition API. As real-world projects scale in complexity, encapsulating features into independent composables allows for seamless logic sharing across components. To push the application further, developers can integrate localStorage for data persistence or adopt Pinia for global state management. This iterative process transforms writing code into a profound practice of Vue 3's core design philosophy.