Build a Todo List App
Create a fully functional todo list application with add, delete, and toggle features.
Overview
In this hands-on tutorial, you'll build a complete Todo List application from scratch. This classic project teaches fundamental React concepts through practical implementation. You'll create a beautiful, responsive interface where users can add tasks, mark them as complete, and delete them. Along the way, you'll master state management, event handling, and component structure - essential skills for any React developer.
🎯 What You'll Learn
- ✓Master React state management using the useState hook
- ✓Handle user inputs and form submissions in React
- ✓Implement CRUD operations (Create, Read, Update, Delete)
- ✓Apply conditional rendering and dynamic CSS classes
- ✓Structure components for maintainability and reusability
- ✓Work with arrays and objects in React state
- ✓Handle keyboard events for better user experience
🚀 Project Features
🛠️ Technologies & Tools
⏱️ Time Breakdown
📊 Difficulty Level: Beginner
This tutorial is perfect for beginners who have just learned React basics. We start with simple concepts and gradually build complexity. Each step is thoroughly explained with clear code examples. No advanced patterns are used - just pure React fundamentals that form the foundation for more complex applications.
💼 Real-World Applications
The skills you learn in this tutorial are used in:
- →Task management systems in project management tools
- →Shopping lists in e-commerce applications
- →Checklist features in productivity apps
- →Assignment trackers in educational platforms
- →Daily routine managers in wellness apps
Prerequisites:
- Basic React knowledge
- Understanding of useState hook
Step 1: Project Setup
Let's start by creating a new React application and setting up our project structure.
1npx create-react-app todo-list-app2cd todo-list-app3npm start45// Create a new file: src/components/TodoList.js
💡 We'll build our todo list as a separate component to keep our code organized.
Step 2: Create the TodoList Component
First, let's create the basic structure of our TodoList component with state management.
1import React, { useState } from 'react';23function TodoList() {4 const [todos, setTodos] = useState([]);5 const [inputValue, setInputValue] = useState('');67 return (8 <div className="todo-list">9 <h1>My Todo List</h1>10 <div className="input-group">11 <input12 type="text"13 value={inputValue}14 onChange={(e) => setInputValue(e.target.value)}15 placeholder="Add a new todo..."16 />17 <button>Add</button>18 </div>19 <ul>20 {todos.map((todo) => (21 <li key={todo.id}>{todo.text}</li>22 ))}23 </ul>24 </div>25 );26}2728export default TodoList;
💡 We use useState to manage our todos array and the input field value. Each todo will be an object with id, text, and completed status.
Step 3: Implement Add Todo Functionality
Now let's add the ability to create new todos when the user clicks the Add button or presses Enter.
1const addTodo = () => {2 if (inputValue.trim() !== '') {3 const newTodo = {4 id: Date.now(),5 text: inputValue,6 completed: false7 };8 setTodos([...todos, newTodo]);9 setInputValue('');10 }11};1213const handleKeyPress = (e) => {14 if (e.key === 'Enter') {15 addTodo();16 }17};1819// Update the input and button:20<input21 type="text"22 value={inputValue}23 onChange={(e) => setInputValue(e.target.value)}24 onKeyPress={handleKeyPress}25 placeholder="Add a new todo..."26/>27<button onClick={addTodo}>Add</button>
💡 We create a unique ID using Date.now() and add the new todo to our state array using the spread operator.
Step 4: Add Toggle and Delete Functions
Let's add the ability to mark todos as complete and delete them.
1const toggleTodo = (id) => {2 setTodos(todos.map(todo =>3 todo.id === id ? { ...todo, completed: !todo.completed } : todo4 ));5};67const deleteTodo = (id) => {8 setTodos(todos.filter(todo => todo.id !== id));9};1011// Update the todo items:12<ul>13 {todos.map((todo) => (14 <li key={todo.id} className={todo.completed ? 'completed' : ''}>15 <input16 type="checkbox"17 checked={todo.completed}18 onChange={() => toggleTodo(todo.id)}19 />20 <span>{todo.text}</span>21 <button onClick={() => deleteTodo(todo.id)}>Delete</button>22 </li>23 ))}24</ul>
💡 We use map to update the completed status and filter to remove todos from the array.
Step 5: Add Styling
Finally, let's add some CSS to make our todo list look great.
1/* Add to src/components/TodoList.css */2.todo-list {3 max-width: 500px;4 margin: 50px auto;5 padding: 20px;6 background: white;7 border-radius: 10px;8 box-shadow: 0 2px 10px rgba(0,0,0,0.1);9}1011.input-group {12 display: flex;13 margin-bottom: 20px;14}1516.input-group input {17 flex: 1;18 padding: 10px;19 border: 2px solid #ddd;20 border-radius: 5px 0 0 5px;21 font-size: 16px;22}2324.input-group button {25 padding: 10px 20px;26 background: #4CAF50;27 color: white;28 border: none;29 border-radius: 0 5px 5px 0;30 cursor: pointer;31}3233.todo-list ul {34 list-style: none;35 padding: 0;36}3738.todo-list li {39 display: flex;40 align-items: center;41 padding: 10px;42 border-bottom: 1px solid #eee;43}4445.todo-list li.completed span {46 text-decoration: line-through;47 opacity: 0.6;48}4950.todo-list li button {51 margin-left: auto;52 background: #f44336;53 color: white;54 border: none;55 padding: 5px 10px;56 border-radius: 3px;57 cursor: pointer;58}
💡 Import this CSS file in your TodoList component to apply the styles.
Complete Code
1import React, { useState } from 'react';2import './TodoList.css';34function TodoList() {5 const [todos, setTodos] = useState([]);6 const [inputValue, setInputValue] = useState('');78 const addTodo = () => {9 if (inputValue.trim() !== '') {10 const newTodo = {11 id: Date.now(),12 text: inputValue,13 completed: false14 };15 setTodos([...todos, newTodo]);16 setInputValue('');17 }18 };1920 const handleKeyPress = (e) => {21 if (e.key === 'Enter') {22 addTodo();23 }24 };2526 const toggleTodo = (id) => {27 setTodos(todos.map(todo =>28 todo.id === id ? { ...todo, completed: !todo.completed } : todo29 ));30 };3132 const deleteTodo = (id) => {33 setTodos(todos.filter(todo => todo.id !== id));34 };3536 return (37 <div className="todo-list">38 <h1>My Todo List</h1>39 <div className="input-group">40 <input41 type="text"42 value={inputValue}43 onChange={(e) => setInputValue(e.target.value)}44 onKeyPress={handleKeyPress}45 placeholder="Add a new todo..."46 />47 <button onClick={addTodo}>Add</button>48 </div>49 <ul>50 {todos.map((todo) => (51 <li key={todo.id} className={todo.completed ? 'completed' : ''}>52 <input53 type="checkbox"54 checked={todo.completed}55 onChange={() => toggleTodo(todo.id)}56 />57 <span>{todo.text}</span>58 <button onClick={() => deleteTodo(todo.id)}>Delete</button>59 </li>60 ))}61 </ul>62 </div>63 );64}6566export default TodoList;
🚀 Extra Challenges
Ready to take this project further? Try these challenges:
- ▪Add a "Clear Completed" button that removes all completed todos
- ▪Implement local storage to persist todos between page refreshes
- ▪Add the ability to edit existing todos
- ▪Create categories or tags for todos
- ▪Add a filter to show all/active/completed todos