ProjectsCore· 120 min read

Project: React To-Do App

Rebuild the to-do app the React way — and feel how much cleaner state-driven UI is.

What you will learn

  • Combine state, events and lists
  • Add and remove items immutably
  • Build a complete interactive component

The brief

Build a to-do app in React: an input to add tasks, a list that renders them, and a delete button on each. Notice you never touch the DOM — you just update state.

A complete React to-do app
function App() {
  const [task, setTask] = useState("");
  const [todos, setTodos] = useState([]);

  function add() {
    if (task.trim() === "") return;
    setTodos([...todos, { id: Date.now(), text: task }]);
    setTask("");
  }
  function remove(id) {
    setTodos(todos.filter(t => t.id !== id));
  }

  return (
    <div>
      <input value={task} onChange={e => setTask(e.target.value)} placeholder="New task" />
      <button onClick={add}>Add</button>
      <ul>
        {todos.map(t => (
          <li key={t.id}>
            {t.text} <button onClick={() => remove(t.id)}>✕</button>
          </li>
        ))}
      </ul>
    </div>
  );
}
Live preview

This small app pulls together state, events and lists. Here is the flow:

  1. Two pieces of state: task (the text being typed) and todos (the list of saved tasks, starting empty).
  2. The input is controlled: value={task} shows the state and onChange keeps task in sync as you type.
  3. Add (Create): clicking Add runs add. It ignores blank input, then setTodos([...todos, { id: Date.now(), text: task }]) makes a new array with the old todos plus the new one (Date.now() gives a unique id). It then clears the input with setTask("").
  4. Show (Read): todos.map(...) renders each todo as an <li> with a unique key={t.id}.
  5. Delete: each button calls remove(t.id), which does setTodos(todos.filter(t => t.id !== id)) — a new array with that one item filtered out.

Note: Output: An input, an Add button, and a growing list. Type “Buy milk”, click Add → it appears with a . Add more, and click any to remove just that one. The page is never touched directly — only state changes.

Tip: Notice the immutable updates: setTodos([...todos, newItem]) to add, and todos.filter(...) to remove. You always create a new array — never mutate the old one.

✍️ Practice

  1. Build the to-do app and confirm add and delete work.
  2. Add a “mark done” toggle that strikes through completed tasks (store a done flag).

🏠 Homework

  1. Add a filter (All / Active / Done) and persist the list in localStorage.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →