Projects›Core· 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.
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:
- Two pieces of state:
task(the text being typed) andtodos(the list of saved tasks, starting empty). - The input is controlled:
value={task}shows the state andonChangekeepstaskin sync as you type. - Add (Create): clicking Add runs
add. It ignores blank input, thensetTodos([...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 withsetTask(""). - Show (Read):
todos.map(...)renders each todo as an<li>with a uniquekey={t.id}. - Delete: each
✕button callsremove(t.id), which doessetTodos(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
- Build the to-do app and confirm add and delete work.
- Add a “mark done” toggle that strikes through completed tasks (store a
doneflag).
🏠 Homework
- Add a filter (All / Active / Done) and persist the list in
localStorage.