Building a To-Do List with Vanilla JavaScript & Firebase

Muhaymin Bin Mehmood

Muhaymin Bin Mehmood

· 4 min read
Building a To-Do List Application with Vanilla JavaScript: From Basics to Advanced with Firebase
Building a To-Do List Application with Vanilla JavaScript: From Basics to Advanced with Firebase

Introduction

To-do list applications are foundational projects for developers. They involve essential concepts like DOM manipulation, event handling, and data storage. In this guide, we’ll build a fully functional to-do list application using Vanilla JavaScript and progressively enhance it by integrating Firebase for real-time database functionality, user authentication, and advanced features like cross-device synchronization.

Table of Contents

  1. Getting Started
    • Setting Up the Project
    • Basic Features of the To-Do List Application
  2. Enhancing the To-Do List
    • Edit, Delete, and Mark as Complete
    • Filtering and Sorting Tasks
  3. Styling the Application
  4. Firebase Integration
    • Setting Up Firebase
    • Adding Real-Time Database Functionality
    • User Authentication and Multi-User Task Management
    • Syncing Data Across Devices
  5. Complete Codebase
  6. Real-World Use Cases and Benefits
  7. FAQs
  8. Conclusion

1. Getting Started

Setting Up the Project

Create the folder structure for your project:

/todo-app  
├── index.html  
├── style.css  
├── script.js  

HTML for the To-Do App

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>To-Do List App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="app">
        <h1>To-Do List</h1>
        <div class="input-container">
            <input type="text" id="taskInput" placeholder="Enter a new task" />
            <button id="addTaskBtn">Add Task</button>
        </div>
        <ul id="taskList"></ul>
    </div>
    <script src="https://www.gstatic.com/firebasejs/9.21.0/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.21.0/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.21.0/firebase-firestore.js"></script>
    <script src="script.js"></script>
</body>
</html>

Basic JavaScript for Adding Tasks

document.getElementById('addTaskBtn').addEventListener('click', function () {
    const taskInput = document.getElementById('taskInput');
    const taskList = document.getElementById('taskList');

    if (taskInput.value.trim() !== '') {
        const li = document.createElement('li');
        li.textContent = taskInput.value;
        taskList.appendChild(li);
        taskInput.value = '';
    } else {
        alert('Please enter a valid task.');
    }
});

2. Enhancing the To-Do List

Adding Edit, Delete, and Mark as Complete

Enhance the task list to include buttons for editing, deleting, and marking tasks as completed.

document.getElementById('addTaskBtn').addEventListener('click', function () {
    const taskInput = document.getElementById('taskInput');
    const taskList = document.getElementById('taskList');

    if (taskInput.value.trim() !== '') {
        const li = document.createElement('li');
        li.innerHTML = `
            <span class="task">${taskInput.value}</span>
            <button class="complete">Complete</button>
            <button class="edit">Edit</button>
            <button class="delete">Delete</button>
        `;
        taskList.appendChild(li);
        taskInput.value = '';
    }

    taskList.addEventListener('click', function (event) {
        if (event.target.classList.contains('delete')) {
            event.target.parentElement.remove();
        } else if (event.target.classList.contains('edit')) {
            const taskText = event.target.parentElement.querySelector('.task');
            const newTask = prompt('Edit your task:', taskText.textContent);
            if (newTask) taskText.textContent = newTask;
        } else if (event.target.classList.contains('complete')) {
            event.target.parentElement.classList.toggle('completed');
        }
    });
});

Filtering and Sorting Tasks

Add a dropdown to filter tasks by their status:

<select id="filter">
    <option value="all">All</option>
    <option value="completed">Completed</option>
    <option value="incomplete">Incomplete</option>
</select>

Update the task filtering logic in JavaScript:

document.getElementById('filter').addEventListener('change', function () {
    const filterValue = this.value;
    const tasks = document.querySelectorAll('#taskList li');
    tasks.forEach(task => {
        const isCompleted = task.classList.contains('completed');
        if (filterValue === 'all' || 
           (filterValue === 'completed' && isCompleted) || 
           (filterValue === 'incomplete' && !isCompleted)) {
            task.style.display = '';
        } else {
            task.style.display = 'none';
        }
    });
});

3. Styling the Application

Add styling for a polished look:

body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    margin: 0;
    padding: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

#app {
    background: #fff;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
    width: 300px;
    text-align: center;
}

.completed {
    text-decoration: line-through;
    color: gray;
}

4. Firebase Integration

Setting Up Firebase

  • Go to the Firebase Console and create a project.
  • Set up a Firestore database and enable Authentication.

Connecting Firebase

Initialize Firebase in script.js:

import { initializeApp } from "firebase/app";
import { getFirestore, collection, addDoc, onSnapshot, deleteDoc, doc } from "firebase/firestore";

const firebaseConfig = {
    apiKey: "YOUR_API_KEY",
    authDomain: "YOUR_AUTH_DOMAIN",
    projectId: "YOUR_PROJECT_ID",
    storageBucket: "YOUR_STORAGE_BUCKET",
    messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
    appId: "YOUR_APP_ID"
};

const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

Adding Real-Time Database Functionality

Store tasks in Firebase:

document.getElementById('addTaskBtn').addEventListener('click', async function () {
    const taskInput = document.getElementById('taskInput');
    if (taskInput.value.trim() !== '') {
        await addDoc(collection(db, "tasks"), { task: taskInput.value, completed: false });
        taskInput.value = '';
    }
});

onSnapshot(collection(db, "tasks"), (snapshot) => {
    const taskList = document.getElementById('taskList');
    taskList.innerHTML = '';
    snapshot.docs.forEach(doc => {
        const task = doc.data();
        const li = document.createElement('li');
        li.innerHTML = `
            <span class="task">${task.task}</span>
            <button class="delete" data-id="${doc.id}">Delete</button>
        `;
        taskList.appendChild(li);

        li.querySelector('.delete').addEventListener('click', async () => {
            await deleteDoc(doc(db, "tasks", li.getAttribute('data-id')));
        });
    });
});

5. Complete Codebase

index.html

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

    <!-- Firebase SDK -->
    <script src="https://www.gstatic.com/firebasejs/9.21.0/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/9.21.0/firebase-firestore.js"></script>
    <script src="script.js"></script>
</body>
</html>

style.css

body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    margin: 0;
    padding: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

#app {
    background: #fff;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
    width: 300px;
    text-align: center;
}

.input-container {
    display: flex;
    justify-content: space-between;
    margin-bottom: 20px;
}

input {
    flex: 1;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    margin-right: 10px;
}

button {
    padding: 10px 15px;
    background-color: #007BFF;
    color: #fff;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

button:hover {
    background-color: #0056b3;
}

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

li {
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    margin-bottom: 10px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.completed {
    text-decoration: line-through;
    color: gray;
}

.delete {
    background-color: #FF5C5C;
    color: #fff;
    border: none;
    padding: 5px 10px;
    cursor: pointer;
    border-radius: 4px;
}

.delete:hover {
    background-color: #FF0000;
}

Script.js

// Import Firebase modules
import { initializeApp } from "firebase/app";
import {
    getFirestore,
    collection,
    addDoc,
    onSnapshot,
    deleteDoc,
    doc,
    updateDoc,
} from "firebase/firestore";

// Firebase configuration
const firebaseConfig = {
    apiKey: "YOUR_API_KEY",
    authDomain: "YOUR_AUTH_DOMAIN",
    projectId: "YOUR_PROJECT_ID",
    storageBucket: "YOUR_STORAGE_BUCKET",
    messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
    appId: "YOUR_APP_ID",
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

// DOM Elements
const taskInput = document.getElementById("taskInput");
const addTaskBtn = document.getElementById("addTaskBtn");
const taskList = document.getElementById("taskList");

// Add Task to Firestore
addTaskBtn.addEventListener("click", async () => {
    const task = taskInput.value.trim();
    if (task) {
        await addDoc(collection(db, "tasks"), {
            task: task,
            completed: false,
        });
        taskInput.value = "";
    } else {
        alert("Please enter a valid task.");
    }
});

// Real-time Listener for Tasks
onSnapshot(collection(db, "tasks"), (snapshot) => {
    taskList.innerHTML = ""; // Clear current tasks
    snapshot.docs.forEach((doc) => {
        const taskData = doc.data();
        const taskId = doc.id;

        // Create task list item
        const li = document.createElement("li");
        li.innerHTML = `
            <span class="task ${taskData.completed ? "completed" : ""}">
                ${taskData.task}
            </span>
            <button class="complete" data-id="${taskId}">Complete</button>
            <button class="delete" data-id="${taskId}">Delete</button>
        `;
        taskList.appendChild(li);

        // Mark Task as Completed
        li.querySelector(".complete").addEventListener("click", async () => {
            await updateDoc(doc(db, "tasks", taskId), {
                completed: !taskData.completed,
            });
        });

        // Delete Task
        li.querySelector(".delete").addEventListener("click", async () => {
            await deleteDoc(doc(db, "tasks", taskId));
        });
    });
});

How to Use This Code

  • Replace "YOUR_API_KEY" and other Firebase configuration values with those from your Firebase project.
  • Copy all three files into a project directory.
  • Open the index.html file in a browser.
  • Add, complete, and delete tasks while the changes sync with Firebase in real-time.

6. Real-World Use Cases and Benefits

Use Cases:

  • Task management apps.
  • Collaboration tools with real-time updates.

Benefits:

  • Learn JavaScript and Firebase integration.
  • Understand real-time database implementation.

7. FAQs

Q: Can I use Firebase Authentication for multiple users?

A: Yes, Firebase Authentication supports multi-user sign-ins using methods like email, Google, and Facebook.

Q: Is Firebase free?

A: Firebase offers a free plan with sufficient limits for small-scale applications.

Now your to-do list app is feature-rich and ready for deployment!

8. Conclusion

Building a To-Do List Application with Vanilla JavaScript and Firebase provides a strong foundation for understanding core web development concepts and real-time database integration. This project demonstrates how simple functionalities like adding, updating, and deleting tasks can evolve into a dynamic, feature-rich application with the addition of Firebase. By following this guide, you’ve learned how to create a fully functional app, write clean and modular code, and use Firebase to sync data in real time.

This project can serve as a starting point for more advanced applications, such as collaborative task managers, note-taking apps, or project management tools. Keep exploring and iterating to add more features, like authentication or cloud storage, to further enhance your skills and portfolio!

Muhaymin Bin Mehmood

About Muhaymin Bin Mehmood

Front-end Developer skilled in the MERN stack, experienced in web and mobile development. Proficient in React.js, Node.js, and Express.js, with a focus on client interactions, sales support, and high-performance applications.

Join our newsletter

Subscribe now to our newsletter for regular updates.

Copyright © 2025 Mbloging. All rights reserved.