From Zero to Hero: Building a ToDo App with React (Step-by-Step)

From Zero to Hero: Building a ToDo App with React (Step-by-Step)

Introduction

From Zero to Hero: Building a ToDo App with React (Step-by-Step). Every developer, whether a beginner or seasoned, has encountered the ToDo app. It’s the perfect starting point to understand a framework like React. The simplicity of a ToDo app makes it an ideal project for learning React concepts like components, props, state, event handling, and conditional rendering.

In this blog, we will walk through building a functional ToDo app using React.js. We’ll explore each step in detail so even if you’re completely new to React, you’ll be able to follow along and build your own project by the end.


1. Setting Up the Project

Before diving into code, we need to set up our development environment.

Prerequisites

Initialize Project with Vite

Vite offers a faster and simpler way to scaffold React projects than Create React App.

npm create vite@latest react-todo -- --template react
cd react-todo
npm install
npm run dev

2. Project Structure

Your initial Vite React app should have the following structure:

react-todo/
├─ public/
├─ src/
│  ├─ App.jsx
│  ├─ index.css
│  └─ main.jsx
├─ index.html
└─ package.json

We’ll organize the app using a components/ folder where we will keep reusable UI parts like the ToDo list and input form.


3. Creating the ToDo Component

Create a new folder called components in the src directory. Inside it, create a file named TodoItem.jsx:

// src/components/TodoItem.jsx
export default function TodoItem({ task, index, onDelete }) {
  return (
    <div className="todo-item">
      <span>{task}</span>
      <button onClick={() => onDelete(index)}>Delete</button>
    </div>
  );
}

This component accepts task, index, and onDelete as props and renders a single ToDo item.


4. Building the Main App Component

Now, let’s use this TodoItem in our App.jsx file:

// src/App.jsx
import { useState } from 'react';
import TodoItem from './components/TodoItem';

function App() {
  const [tasks, setTasks] = useState([]);
  const [input, setInput] = useState('');

  const addTask = () => {
    if (input.trim() === '') return;
    setTasks([...tasks, input]);
    setInput('');
  };

  const deleteTask = (index) => {
    const newTasks = [...tasks];
    newTasks.splice(index, 1);
    setTasks(newTasks);
  };

  return (
    <div className="app">
      <h1>My ToDo List</h1>
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
        placeholder="Enter a task"
      />
      <button onClick={addTask}>Add</button>
      {tasks.map((task, index) => (
        <TodoItem key={index} task={task} index={index} onDelete={deleteTask} />
      ))}
    </div>
  );
}

export default App;

5. Styling the App

Add some basic CSS in index.css:

.app {
  text-align: center;
  padding: 20px;
  font-family: Arial, sans-serif;
}
input {
  padding: 10px;
  width: 200px;
  margin-right: 10px;
}
button {
  padding: 10px 15px;
  background-color: #61dafb;
  border: none;
  cursor: pointer;
}
.todo-item {
  margin-top: 10px;
  display: flex;
  justify-content: center;
  gap: 10px;
}

Feel free to enhance this with custom themes or animations.


6. Bonus: Mark Task as Complete

To make our app more interactive, let’s add the ability to mark a task as complete. Update the tasks state to hold objects instead of strings:

const [tasks, setTasks] = useState([]);

const addTask = () => {
  if (input.trim() === '') return;
  setTasks([...tasks, { text: input, completed: false }]);
  setInput('');
};

const toggleTask = (index) => {
  const newTasks = [...tasks];
  newTasks[index].completed = !newTasks[index].completed;
  setTasks(newTasks);
};

Update TodoItem.jsx:

export default function TodoItem({ task, index, onDelete, onToggle }) {
  return (
    <div className="todo-item">
      <span
        onClick={() => onToggle(index)}
        style={{ textDecoration: task.completed ? 'line-through' : 'none' }}
      >
        {task.text}
      </span>
      <button onClick={() => onDelete(index)}>Delete</button>
    </div>
  );
}

And finally update the App.jsx render logic:

{tasks.map((task, index) => (
  <TodoItem
    key={index}
    task={task}
    index={index}
    onDelete={deleteTask}
    onToggle={toggleTask}
  />
))}

7. Persisting Data with LocalStorage (Optional)

To save tasks even after refreshing the browser, use localStorage:

useEffect(() => {
  const saved = JSON.parse(localStorage.getItem('tasks'));
  if (saved) setTasks(saved);
}, []);

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

You’ll need to import useEffect from React:

import { useState, useEffect } from 'react';

8. Hosting Your App Online

Once done, you can deploy your ToDo app online for free:

  • Use Netlify for simple drag and drop deployment.
  • Or use Vercel which directly integrates with GitHub repositories.

Conclusion

Congratulations! 🎉 You just built a complete ToDo app in React from scratch. Along the way, you learned about:

  • React project structure
  • JSX and components
  • State and props
  • Event handling
  • Conditional rendering
  • Local storage and persistence

This project is not just a beginner milestone—it’s also a great portfolio piece. You can extend it with more features like categories, due dates, dark mode, or even Firebase authentication.

React is a powerful library, and this ToDo app is just the beginning of what you can create with it. So keep building and keep learning!


Stay tuned for more React tutorials and project ideas. If you liked this blog, feel free to share it and build your version of the app!

Find more React content at: https://allinsightlab.com/category/software-development

Leave a Reply

Your email address will not be published. Required fields are marked *