One of the better starting tasks to learn JavaScript is building a to-do app. This article will create a basic TO-DO app saving task in the LocalStorage on the browser. This means that your tasks persist even if you refresh the website.

LocalStorage: Why is this necessary?

Built-in in the browser feature lets you save data as key-value pairs. This data never expires, hence it stays accessible even after a page refresh or browser restart.

Let us start right now.

Project Structure

To handle our to-do list, we will design a simple HTML structure with JavaScript.

1. HTML Structure

Create an index.html file with this structure:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>To-Do App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="app">
        <h1>To-Do List</h1>
        <form id="todo-form">
            <input type="text" id="todo-input" placeholder="Add a new task..." required>
            <button type="submit">Add</button>
        </form>
        <ul id="todo-list"></ul>
    </div>

    <script src="app.js"></script>
</body>
</html>

Code breakdown:

We use a form to insert tasks so the user can press enter to add one.

Input Field: The user cannot submit an empty task since the element needed is required.

Task List: The element holds dynamically the tasks.

Script: We link to a JavaScript that handles all the logic (app.js).

2.Basic CSS

Create a style.css file to style the app:

body {
  font-family: Arial, sans-serif;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  margin: 0;
  background-color: #f4f4f4;
}
ul {
  list-style-type: none;
  padding: 0;
}
.app {
  background: white;
  padding: 20px;
  border-radius: 5px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  width: 600px;
}
#todo-form {
  display: flex;
  align-items: center;
  border: 1px solid #ddd;
  border-radius: 5px;
  overflow: hidden;
}
#todo-form:focus-within {
  border-color: #333;
}

button {
  background: #333;
  color: white;
  border: none;
  padding: 10px 20px;
  cursor: pointer;
}
#todo-input {
  flex: 1;
  padding: 10px;
  border: none;
  outline: none;
}
#todo-list li {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px;
    border-bottom: 1px solid #ddd;
  
}
button.delete {
  background: red;
  color: white;
  border: none;
  padding: 5px 10px;
  cursor: pointer;
}

3. JavaScript: Adding Functionality

Create an app.js file.

  • Choose DOM elements,

    First, we need to grab the HTML elements we intend to change with JavaScript:

    const todoForm = document.getElementById('todo-form');
    const todoInput = document.getElementById('todo-input');
    const todoList = document.getElementById('todo-list');

    This lets us interact with the form, input field, and task list.

  • load LocalStorage tasks.

    We load tasks from LocalStorage when the application starts to make sure they persist after refreshing.

    const todos = JSON.parse(localStorage.getItem('todos')) || [];

    localStorage.GetItem('todos') gets the previously saved tasks.

    JSON.parse() turns the string back into an array.

    If no tasks exist, an empty array returns.

  • Render current tasks.

    Next, we need a function to show the tasks on the screen:

    function renderTodos() {
        // Clear the current list to avoid duplication
        todoList.innerHTML = '';
        
        // Loop through each todo and create list items
        todos.forEach((todo, index) => {
            const li = document.createElement('li');  // Create a new list item
            li.textContent = todo;  // Set the text of the list item to the todo value
            
            // Create a delete button for each todo
            const deleteBtn = document.createElement('button');
            deleteBtn.textContent = 'Delete';  // Set button text to 'Delete'
            deleteBtn.classList.add('delete');  // Add a class to style the button
            
            // Add click event to the delete button
            deleteBtn.addEventListener('click', () => deleteTodo(index));
            
            // Append the delete button to the list item
            li.appendChild(deleteBtn);
            
            // Append the list item to the todo list in the DOM
            todoList.appendChild(li);
        });
    }
    • Why is this stage important? Task appearances won't show without rendering.
    • We clean the list to avoid duplicates.
    • Each task gets a delete button to click to remove it.
  • Add New Tasks

    When the user submits the form, we need to add the new task to LocalStorage and render it:

    function addTodo(e) {
        e.preventDefault();  // Prevent form from refreshing the page on submit
        
        // Get the value from the input and remove whitespace from both ends
        const newTodo = todoInput.value.trim();
        
        // If the input is empty, do nothing (avoid adding empty tasks)
        if (newTodo === '') return;
    
        // Add the new task to the array of todos
        todos.push(newTodo);
        
        // Save the updated todos array to LocalStorage as a JSON string
        localStorage.setItem('todos', JSON.stringify(todos));
        
        // Clear the input field after adding the task
        todoInput.value = '';
        
        // Re-render the task list to include the new task
        renderTodos();
    }
    • e.preventDefault() stops the form from page refresh.
    • We trim the input and avoid pointless tasks.
    • Converting the tasks to a string allows LocalStorage save them.
  • Delete Tasks

    Clicking the delete button should delete the related task.

    function deleteTodo(index) {
        // Remove the todo item from the array at the specified index
        todos.splice(index, 1);
        
        // Update LocalStorage with the new array (after deletion)
        localStorage.setItem('todos', JSON.stringify(todos));
        
        // Re-render the updated list of tasks
        renderTodos();
    }
  • Initial Rendering

    Now, we call the ā£renderTodos function on page load to display saved tasks.

    renderTodos();
  • Delete Tasks

    Clicking the delete button should delete the related task.

    todoForm.addEventListener('submit', addTodo)

    This completes the app's core functionality!

View full code and download it on GitHub 

Possibly asked questions 

Why do we use LocalStorage?

LocalStorage improves user experience by letting tasks remain even after page refreshing, therefore preventing data loss.


How can I store JavaScript tasks to LocalStorage?

JSON.stringify() will transform the task array before it goes to storage; localStorage.setItem() will save tasks as a JSON string.


Can I delete LocalStorage tasks?

Indeed, the splice() method allows us to delete an item from the array, then update LocalStorage and re-render the list.


When the website loads, how can I load LocalStorage tasks?

Retrieve the tasks from localStorage.getitem() and JSON-parse them. Should no tasks exist, create an empty array.


Could the to-do app have more features?

Correct! To grow the app, include task editing, due dates, drag-and-drop capability, and filtering by completed tasks.