Building a Todo App with React, Vite, and Tailwind CSS

Shikhar ShuklaShikhar Shukla
4 min read

In this tutorial, we will build a simple, yet functional, Todo App using React, Vite, and Tailwind CSS. If you're looking to quickly prototype or create a polished application, these tools offer a great combination of speed, flexibility, and ease of use.

Why React, Vite, and Tailwind?

  • React is a powerful library for building user interfaces, and its component-based architecture makes it perfect for handling individual UI features.

  • Vite is a next-generation frontend tool that significantly speeds up the development process by providing instant server starts and lightning-fast HMR (Hot Module Replacement).

  • Tailwind CSS is a utility-first CSS framework that allows you to build modern, responsive designs with ease and flexibility, without writing custom CSS from scratch.

Setting Up the Project with Vite

First, let's set up our project using Vite.

  1. Open your terminal and run:

     npm create vite@latest todo-app
     cd todo-app
     npm install
    
  2. Next, install the necessary dependencies for React and Tailwind CSS:

     npm install react react-dom
     npm install -D tailwindcss postcss autoprefixer
     npx tailwindcss init
    
  3. Configure Tailwind by adding the following to your tailwind.config.js file:

     module.exports = {
       content: [
         './index.html',
         './src/**/*.{js,jsx,ts,tsx}',
       ],
       theme: {
         extend: {},
       },
       plugins: [],
     }
    
  4. Now, include Tailwind CSS in your index.css:

     @tailwind base;
     @tailwind components;
     @tailwind utilities;
    
  5. You're ready to start developing the app! Run npm run dev to start the development server.

Building the Todo App

Let’s break down the Todo app step by step.

1. Creating the Main TodoApp Component

The TodoApp component will manage the core functionality of the app: adding, displaying, and managing todos.

import { useState, useEffect } from "react";
import { v4 as uuidv4 } from "uuid";
import TodoInput from "./TodoInput";
import TodoList from "./TodoList";

function TodoApp() {
  const [todo, setTodo] = useState("");
  const [todos, setTodos] = useState([]);
  const [showFinished, setShowFinished] = useState(true);

  useEffect(() => {
    const todoString = localStorage.getItem("todos");
    if (todoString) {
      setTodos(JSON.parse(todoString));
    }
  }, []);

  useEffect(() => {
    localStorage.setItem("todos", JSON.stringify(todos));
  }, [todos]);

  const handleAdd = (newTodo) => {
    setTodos([...todos, { id: uuidv4(), todo: newTodo, isCompleted: false }]);
    setTodo("");
  };

  const toggleFinished = () => setShowFinished(!showFinished);

  return (
    <div className="container mx-auto md:my-5 rounded-md shadow-2xl p-5 bg-blue-200 md:min-h-[90vh] md:w-2/5 w-full">
      <div className="flex justify-center bg-blue-900 text-white py-2 rounded-md">
        <span className="font-bold text-xl mx-9">Todo App</span>
      </div>
      <TodoInput todo={todo} setTodo={setTodo} handleAdd={handleAdd} />
      <div className="text-center">
        <input
          type="checkbox"
          checked={showFinished}
          onChange={toggleFinished}
        /> Show Finished
      </div>
      <TodoList todos={todos} showFinished={showFinished} />
    </div>
  );
}

export default TodoApp;

2. Todo Input Component

This component handles the input for adding new todos.

function TodoInput({ todo, setTodo, handleAdd }) {
  const handleChange = (e) => setTodo(e.target.value);

  return (
    <div className="addTodo my-5 text-center">
      <h2 className="text-xl font-bold">Add a Todo Item</h2>
      <input
        type="text"
        className="w-full mt-5 rounded-md p-2"
        value={todo}
        onChange={handleChange}
      />
      <button
        onClick={() => handleAdd(todo)}
        className="bg-blue-800 hover:bg-blue-900 text-white p-2 py-1 my-6 rounded-md font-bold mx-6 w-20"
        disabled={todo.length === 0}
      >
        Save
      </button>
    </div>
  );
}

export default TodoInput;

3. Todo List Component

This component displays all the todos and allows marking them as completed or deleting them.

function TodoList({ todos, showFinished }) {
  const handleCheckbox = (id) => {
    let index = todos.findIndex((item) => item.id === id);
    let newTodos = [...todos];
    newTodos[index].isCompleted = !newTodos[index].isCompleted;
    setTodos(newTodos);
  };

  const handleDelete = (id) => {
    setTodos(todos.filter((item) => item.id !== id));
  };

  return (
    <div className="todos">
      {todos.length === 0 && <p className="m-5">Nothing to Do!</p>}
      {todos
        .filter((item) => showFinished || !item.isCompleted)
        .map((item) => (
          <div
            key={item.id}
            className={`todo flex w-full my-3 justify-between items-center ${
              item.isCompleted ? "bg-green-200" : "bg-blue-100"
            } p-3 border-2 border-blue-300 rounded-md hover:bg-blue-300`}
          >
            <input
              type="checkbox"
              checked={item.isCompleted}
              onChange={() => handleCheckbox(item.id)}
            />
            <div className={item.isCompleted ? "line-through" : ""}>
              {item.todo}
            </div>
            <button
              onClick={() => handleDelete(item.id)}
              className="bg-blue-800 hover:bg-blue-900 text-white p-2 rounded-md"
            >
              Delete
            </button>
          </div>
        ))}
    </div>
  );
}

export default TodoList;

4. Adding Tailwind CSS for Styling

Thanks to Tailwind CSS, we can quickly style our components without writing traditional CSS. Throughout the app, we use utility classes like bg-blue-200, rounded-md, and text-center to create a clean and responsive design.

Conclusion

And that’s it! You now have a fully functional Todo app built using React, Vite, and Tailwind CSS. This setup not only makes your development process fast but also helps in maintaining a clean and scalable codebase.

Feel free to experiment and extend this app by adding more features like filtering, search, or even connecting it to a backend service.


https://todoappreactss.netlify.app

I hope this article helps you get started with React, Vite, and Tailwind CSS. Let me know your thoughts or any improvements you make to your Todo app!

0
Subscribe to my newsletter

Read articles from Shikhar Shukla directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shikhar Shukla
Shikhar Shukla