Building a Todo App with Theme Toggle Using React
Introduction
In this tutorial, we'll build a Todo List Web Application using React. This project helps in understanding state management, event handling, and working with lists in React. It’s perfect for beginners looking to strengthen their skills in React development.
Project Overview
The Todo List application allows users to add, mark as completed, and remove tasks. It offers a clean interface for managing daily tasks. This project showcases how React can be used to manage the state of a simple yet dynamic application.
Features
Add New Task: Users can add tasks to the list.
Mark as Completed: Users can mark tasks as done.
Delete Task: Users can delete tasks from the list.
Local Storage: Persists tasks across page reloads using
localStorage
.
Technologies Used
React: For building the user interface and managing component state.
CSS: For styling the application.
JavaScript: For the core logic and functionality.
Project Structure
The project structure follows a typical React project layout:
├── public
├── src
│ ├── components
│ │ ├── TodoList.jsx
│ │ ├── TodoItem.jsx
│ ├── App.jsx
│ ├── App.css
│ ├── index.js
│ └── index.css
├── package.json
└── README.md
Key Components
TodoList.jsx: Handles the display and management of the todo list.
TodoItem.jsx: Manages individual todo items, including marking them as completed or deleting them.
Code Explanation
TodoList Component
This component handles the state of the entire todo list, including adding new tasks and rendering the list.
import { useState, useEffect } from "react";
import TodoItem from "./TodoItem";
const TodoList = () => {
const [task, setTask] = useState("");
const [tasks, setTasks] = useState([]);
useEffect(() => {
const savedTasks = JSON.parse(localStorage.getItem("tasks")) || [];
setTasks(savedTasks);
}, []);
useEffect(() => {
localStorage.setItem("tasks", JSON.stringify(tasks));
}, [tasks]);
const addTask = () => {
if (task.trim()) {
setTasks([...tasks, { text: task, completed: false }]);
setTask("");
}
};
const toggleCompletion = (index) => {
const newTasks = tasks.map((t, i) =>
i === index ? { ...t, completed: !t.completed } : t
);
setTasks(newTasks);
};
const deleteTask = (index) => {
const newTasks = tasks.filter((_, i) => i !== index);
setTasks(newTasks);
};
return (
<div className="todo-list">
<h1>Todo List</h1>
<input
type="text"
value={task}
onChange={(e) => setTask(e.target.value)}
placeholder="Add a new task"
/>
<button onClick={addTask}>Add Task</button>
<ul>
{tasks.map((t, index) => (
<TodoItem
key={index}
task={t}
index={index}
toggleCompletion={toggleCompletion}
deleteTask={deleteTask}
/>
))}
</ul>
</div>
);
};
export default TodoList;
TodoItem Component
The TodoItem
component manages the display of each task, along with options to mark it as completed or delete it.
const TodoItem = ({ task, index, toggleCompletion, deleteTask }) => {
return (
<li className={task.completed ? "completed" : ""}>
<span onClick={() => toggleCompletion(index)}>{task.text}</span>
<button onClick={() => deleteTask(index)}>Delete</button>
</li>
);
};
export default TodoItem;
In this component, we receive props from the parent TodoList
and handle actions like toggling task completion or deleting the task.
App Component
The App.jsx
serves as the root of the application, rendering the TodoList
component.
import { useState } from "react";
import "./App.css";
import TodoList from './components/TodoList';
import sun from "./assets/images/icon-sun.svg";
import moon from "./assets/images/icon-moon.svg";
const App = () => {
const [isLightTheme, setIsLightTheme] = useState(false);
const toggleTheme = () => {
setIsLightTheme(!isLightTheme);
};
return (
<div className={isLightTheme ? "light-theme" : ""}>
<div className="app">
<div className="header">
<div className="title">
<h1>TODO</h1>
</div>
<div className="mode" onClick={toggleTheme}>
<img src={isLightTheme ? moon : sun} alt="theme-icon" />
</div>
</div>
<Todo />
<div className="footer">
<p>Made with ❤️ by Abhishek Gurjar</p>
</div>
</div>
</div>
);
};
export default App;
CSS Styling
The CSS ensures the Todo List application is user-friendly and responsive.
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: Josefin Sans, sans-serif;
}
.app {
width: 100%;
height: 100vh;
background-color: #161722;
color: white;
background-image: url(./assets//images/bg-desktop-dark.jpg);
background-repeat: no-repeat;
background-size: contain;
background-position-x: center;
background-position-y: top;
display: flex;
align-items: center;
justify-content: flex-start;
flex-direction: column;
}
.header {
width: 350px;
margin-top: 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
.title h1 {
font-size: 30px;
letter-spacing: 7px;
}
.mode {
display: flex;
align-items: center;
justify-content: center;
}
.mode img {
width: 22px;
}
.todo {
width: 350px;
flex-direction: column;
display: flex;
align-items: center;
justify-content: flex-start;
}
.input-box {
border-bottom: 1px solid white;
display: flex;
align-items: center;
justify-content: center;
background-color: #25273c;
width: 100%;
gap: 10px;
padding: 8px;
border-radius: 10px;
}
.check-circle {
width: 12px;
height: 12px;
border-radius: 50%;
border: 1px solid white;
display: flex;
align-items: center;
justify-content: center;
background-image: linear-gradient(to right,hsl(230, 50%, 20%) , hsl(280, 46%, 28%));
}
.input-task {
width: 90%;
border: none;
color: white;
background-color: #25273c;
}
.input-task:focus {
outline: none;
}
.todo-list {
margin-top: 20px;
width: 350px;
background-color: #25273c;
}
.todo-box {
margin-inline: 15px;
margin-block: 10px;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
gap: 15px;
}
.todo-box .cross{
width: 14px;
}
.details {
margin-bottom: 40px;
border-bottom: 1px solid white;
width: 350px;
display: flex;
align-items: center;
justify-content: space-evenly;
background-color: #25273c;
font-size: 12px;
padding: 12px;
border-bottom-right-radius: 7px;
border-bottom-left-radius: 7px;
}
.details .clickBtn{
cursor: pointer;
}
.details .clickBtn:hover{
color: #3074fd;
}
/* //light Theme */
.light-theme .app {
background-color: #fff;
color: #000;
background-image: url(./assets//images/bg-desktop-light.jpg);
}
.light-theme .header {
color: white;
}
.light-theme .input-box{
background-color: white;
color: black;
border-bottom: 1px solid black;
}
.light-theme input{
background-color: white;
color: black;
}
.light-theme .check-circle{
border:1px solid black;
}
.light-theme .todo-list{
background-color: white;
color: black;
}
.light-theme .details{
border-bottom: 1px solid black;
background-color: white;
color: black;
}
.footer{
margin: 40px;
}
The styles ensure the Todo List is simple and clean while allowing for task management.
Installation and Usage
To get started, clone the repository and install the dependencies:
git clone https://github.com/abhishekgurjar-in/todo-app.git
cd todo-list
npm install
npm start
The application will start running at http://localhost:3000
.
Live Demo
Check out the live demo of the Todo List here.
Conclusion
The Todo List project is a great way to practice working with state, lists, and event handling in React. It demonstrates how to build a useful application that can persist data across sessions using localStorage
.
Credits
- Inspiration: Inspired by the need for a simple and effective task management tool.
Author
Abhishek Gurjar is a passionate web developer. You can check out more of his projects on GitHub.
Subscribe to my newsletter
Read articles from Abhishek Gurjar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Abhishek Gurjar
Abhishek Gurjar
I’m Abhishek Gurjar, a Full Stack MERN developer specializing in MongoDB, Express.js, React, and Node.js. I love creating dynamic web apps and sharing insights on web development.