Create TODO Application using Vanilla JS and Redux!

October 17, 202023 min read


In this blog, I am going to show you how to create a simple todo application using vanilla JavaScript and Redux. This is a great way to learn the basics of Redux and how to use it in a practical way.

Before Staring

Before we begin, let’s go over some key concepts and terms that will be used throughout this tutorial:

  • Actions: An action is a plain javascript object that describes a change in the application’s state. It typically contains a type property that describes the type of action and any additional data that may be needed to make the change.
  • Reducers: A reducer is a pure function that takes in the current state and an action, and returns a new state based on the action. It is responsible for updating the state of the application based on the actions it receives.
  • Store: The store is the central repository for all of the application’s state. It is created by passing the root reducer to the createStore() method provided by redux.
  • Dispatch: Dispatching an action is the process of sending an action to the store so that it can be handled by the reducers and the state can be updated.
Getting Started

First we will generate a vanilla js project using vite.

Run npm create vite@latest todo-app and select Vanilla as Framework.

Next, we need to install the Redux library. We can do this by running the following command in our terminal:

npm install redux --save

Next, we need to create our Redux store. The store holds the state of our application and allows us to dispatch actions that change the state.

Create a file with name store.js and add following code in it.

// store.js
import { createStore } from "redux"

// Our initial state
const initialState = {
  todoArr: [],

// Our reducer
const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "ADD_TODO":
      // Add a new todo to the list
      return {
        todoArr: [...state.todoArr, action.payload],
    case "REMOVE_TODO":
      // Remove a todo from the list
      return {
        todoArr: state.todoArr.filter(todo => !== action.payload),
      return state

// Create our store
const store = createStore(reducer)

export default store

In the code above, we have defined our initial state and our reducer. The reducer takes the current state and an action, and returns a new state based on the action. In our case, we have two actions: ADD_TODO and REMOVE_TODO.

Now, let’s create our HTML page and add some basic styles.

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Todo App</title>
      <h1 class="header">Todo List</h1>
      <form class="form">
        <div class="form-element">
          <label for="title">Title</label>
            placeholder="Pickup Groceries"
        <div class="form-element">
          <label for="summary">Summary</label>
            placeholder="Don't forgat to add milk and eggs"
        <button type="submit">Add Task</button>
      <div id="app"></div>
    <script type="module" src="/main.js"></script>

In code above we have created a basic form and a app container for todo list.

Now let’s add the logic to add items to our todo list.

// index.js
import "./style.css"
import store from "./store"

// Get the app container
const form = document.querySelector("form")

form.addEventListener("submit", event => {
  const formData = new FormData(form)
  const dataObj = {}
  for (const [key, value] of formData) {
    dataObj[key] = value
  if (!dataObj.title) return

    type: "ADD_TODO",
    payload: dataObj,

store.subscribe(() => {

const app = document.querySelector("#app")
const listEl = document.createElement("ol")
listEl.addEventListener("click", e => {
  const action =
  const index = parseInt(
    type: `${action}_TODO`,
    payload: index,

const generateTodoList = list => {
  const htmlList =
    (todo, i) =>
      <li class="todo-list-item" >
          ? `<div class="checkmark">&#10004;</div>`
          : `<button data-action="complete" data-index="${i}" title="Complete Task" class="remove-button">&#10004;</button>`
        <div class="todo-item">
          <p class="todo-title">${todo.title}</p>
          ${todo.summary ? `<p class="todo-summary">${todo.summary}</p>` : ""}
        <button data-action="remove" data-index="${i}" title="Remove Task" class="remove-button">&#9747;</button>
  listEl.innerHTML = htmlList.join("")

In code above we have added an event listener for form submit event. Where we are extracting data from the form and dispatching an event ADD_TODO to update store.

Then we are subscribing to store changes so that we can update todo list whenever store updates.

At last we have created a basic template to render todo item and added an event listener to mark Item as complete or Remove the Item.

Then we will add some css to style the app.

:root {
  font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 24px;
  font-weight: 400;

  color-scheme: light dark;
  color: rgba(255, 255, 255, 0.87);
  background-color: #1a202c;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;

h1 {
  font-size: 2em;
  line-height: 1.1;

button {
  border-radius: 8px;
  border: 1px solid transparent;
  padding: 0.6em 1.2em;
  font-size: 1em;
  font-weight: 500;
  font-family: inherit;
  background-color: #0c6c33;
  cursor: pointer;
  transition: background-color 0.25s;
button:hover {
  background-color: #179848;
button:focus-visible {
  outline: 4px auto -webkit-focus-ring-color;

@media (prefers-color-scheme: light) {
  :root {
    color: #1a202c;
    background-color: #ffffff;
  button {
    background-color: #f9f9f9;
  .todo-item {
    background-color: #1a202c;
    color: rgba(255, 255, 255, 0.87);

.header {
  text-align: center;
  margin-top: 2rem;

.form {
  margin: auto;
  width: 20rem;

.form-element {
  display: flex;
  flex-direction: column;
  margin-bottom: 1.25rem;
label {
  margin-bottom: 0.75rem;

textarea {
  padding: 0.75rem 1rem;
  border-radius: 0.25rem;
  border: none;

#app {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;

.todo-list {
  margin: 20px 0;
  padding: 0;
  width: 100%;
  max-width: 40rem;
  list-style: none;

.todo-list-item {
  display: flex;
  align-items: self-start;
  gap: 1rem;
  padding: 1rem;
  margin-bottom: 10px;
  border-radius: 5px;
  background-color: rgba(255, 255, 255, 0.87);

.todo-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  color: #1a202c;

.todo-title {
  margin: 0;
  font-size: 1.5rem;

.todo-summary {
  margin: 0;
  margin-top: 0.5rem;

.remove-button {
  padding: 0.25rem;
  border-radius: 4px;
  line-height: 1;

.checkmark {
  color: #0c6c33;

That’s it. We have created a todo app in Vanilla JS using redux. Next you can add some logic to update a task or store tasks in localstorage for persistance.

Thanks & Keep Coding.

Vishal Sharma

Hey there! This is Vishal Sharma. I reside and work at Gurgaon, India. I am a Software Engineer and primarily works with JavaScript, ReactJS and NodeJS.
LinkedIn Link

Welcome to my Javascript tech blog! Here, you'll find the latest news and updates in the world of Javascript, as well as tutorials and resources to help you improve your coding skills. From learning the basics of Javascript to advanced concepts like object-oriented programming, I post something for developers of all levels. Whether you're just starting out or you're a seasoned pro, you'll find valuable insights and information on our blog. So stay up-to-date with the latest in Javascript technology by bookmarking this page and checking back often.
Thank you for visiting!