Build a Todo List Project in React

Learn how to build a Todo List App in React step by step. Understand state management, CRUD operations, localStorage, components, hooks, and project structure.
Build a Todo List Project in React: Complete Beginner Guide
One of the best projects for learning React is a Todo List Application.
It may look simple, but a Todo App teaches many important React concepts, including:
- Components
- State Management
- Event Handling
- Conditional Rendering
- Lists and Keys
- Form Handling
- Local Storage
- CRUD Operations
This is why Todo List projects are often recommended as the first real React project.
In this guide, you'll build a complete Todo List application and understand how React works in real-world scenarios.
Prerequisites
Before building this project, make sure you understand:
These concepts are used throughout the project.
What Will We Build?
Our Todo Application will support:
✅ Add Tasks
✅ Display Tasks
✅ Mark Tasks as Complete
✅ Delete Tasks
✅ Persist Data Using Local Storage
Example:
📝 Learn React
📝 Build Todo App
✅ Complete Project
🗑 Delete Task
Why Build a Todo List Project?
A Todo App combines many fundamental React concepts into one project.
You'll learn:
- managing state
- updating UI dynamically
- handling user input
- rendering lists
- storing data locally
These skills apply to almost every React application.
Project Structure
src
├── components
│ ├── TodoForm.jsx
│ ├── TodoList.jsx
│ └── TodoItem.jsx
├── App.jsx
└── main.jsx
This structure keeps the project organized and scalable.
Step 1: Create State
Open:
App.jsx
Create state for todos:
import { useState } from "react";
function App() {
const [todos, setTodos] =
useState([]);
}
This state will store all tasks.
Understanding Todo Data
Each task can be stored as:
{
id: 1,
text: "Learn React",
completed: false
}
This structure makes task management easier.
Step 2: Create Todo Input
import { useState } from "react";
function TodoForm({
addTodo
}) {
const [text, setText] =
useState("");
const handleSubmit = (e) => {
e.preventDefault();
if (!text.trim())
return;
addTodo(text);
setText("");
};
return (
<form
onSubmit={handleSubmit}
>
<input
type="text"
placeholder="Add Todo"
value={text}
onChange={(e) =>
setText(
e.target.value
)
}
/>
<button>
Add
</button>
</form>
);
}
export default TodoForm;
This component handles user input.
Step 3: Add New Todos
Inside App.jsx:
const addTodo = (text) => {
const newTodo = {
id: Date.now(),
text,
completed: false
};
setTodos([
...todos,
newTodo
]);
};
This function adds a new task.
Step 4: Render Todos
{
todos.map((todo) => (
<div
key={todo.id}
>
{todo.text}
</div>
))
}
React automatically updates the UI when tasks change.
Step 5: Create Todo Item Component
function TodoItem({
todo
}) {
return (
<div>
{todo.text}
</div>
);
}
export default TodoItem;
Using separate components improves maintainability.
Step 6: Mark Tasks Complete
const toggleTodo = (id) => {
setTodos(
todos.map((todo) =>
todo.id === id
? {
...todo,
completed:
!todo.completed
}
: todo
)
);
};
This toggles task completion status.
Updating the UI
<span
style={{
textDecoration:
todo.completed
? "line-through"
: "none"
}}
>
{todo.text}
</span>
Completed tasks become visually distinct.
Step 7: Delete Tasks
const deleteTodo = (id) => {
setTodos(
todos.filter(
(todo) =>
todo.id !== id
)
);
};
This removes a task permanently.
Delete Button
<button
onClick={() =>
deleteTodo(
todo.id
)
}
>
Delete
</button>
A common feature in every Todo application.
Step 8: Save Todos Using localStorage
Without storage:
Refresh
↓
Todos Lost
Use localStorage:
useEffect(() => {
localStorage.setItem(
"todos",
JSON.stringify(
todos
)
);
}, [todos]);
This saves tasks automatically.
Step 9: Load Saved Todos
useEffect(() => {
const savedTodos =
JSON.parse(
localStorage.getItem(
"todos"
)
);
if (savedTodos) {
setTodos(
savedTodos
);
}
}, []);
Now tasks remain after page refreshes.
Complete App Flow
Add Todo
↓
Store in State
↓
Render List
↓
Mark Complete
↓
Save to Storage
↓
Restore on Refresh
This mirrors real-world React application behavior.
Final Application Features
Your Todo App now supports:
- adding tasks
- viewing tasks
- completing tasks
- deleting tasks
- local storage persistence
These are core CRUD operations.
Styling with Tailwind CSS
Example:
<button
className="
bg-blue-600
text-white
px-4
py-2
rounded
"
>
Add
</button>
Tailwind makes it easy to create a modern UI.
Real Project Improvements
Once the basic project works, add:
- edit tasks
- task categories
- due dates
- dark mode
- drag and drop
- search functionality
- task filters
These features simulate production applications.
Common Beginner Mistakes
Mutating State Directly
Incorrect:
todos.push(newTodo);
Always use:
setTodos(...)
Forgetting Keys
Incorrect:
todos.map(todo => (
<div>
{todo.text}
</div>
))
Always add:
key={todo.id}
Not Using Local Storage
Users expect data to remain after refreshes.
Creating Large Components
Split functionality into smaller reusable components.
What Concepts Does This Project Teach?
This single project covers:
- React Components
- Props
- State
- Hooks
- Event Handling
- Conditional Rendering
- Lists
- Forms
- Local Storage
- CRUD Operations
That's why Todo Apps are a popular learning project.
Follow Along With the Video Tutorial
If you prefer building projects by watching and coding together, check out the complete React Todo List Project tutorial on YouTube:
👉 Watch the React Todo App Tutorial
The video walks through component creation, state management, localStorage integration, and UI improvements step by step.
Build Something More Advanced
After completing this Todo App, try building:
- Notes App
- Expense Tracker
- Habit Tracker
- Task Management App
- Project Management Dashboard
These projects build on the same React concepts.
Production Tip
Professional React developers usually:
- separate components properly
- store data efficiently
- avoid unnecessary re-renders
- create reusable UI elements
- organize state logically
Building projects is the fastest way to improve React skills.
Why Todo Apps Matter
A Todo List project may seem simple, but it teaches the foundations of React development.
Many real-world applications use the same concepts on a larger scale.
Learning to build a Todo App prepares you for more advanced projects.
Conclusion
Building a Todo List Application is one of the best ways to practice React fundamentals.
By creating features such as adding, deleting, completing, and persisting tasks, you'll gain hands-on experience with state management, components, hooks, and local storage.
A well-built Todo App is an excellent stepping stone toward building larger and more complex React applications.