DEV Community

Cover image for ✨ React To-Do App: A Beginner’s Thinking Guide to Building from Scratch
Srushti Patil
Srushti Patil

Posted on

✨ React To-Do App: A Beginner’s Thinking Guide to Building from Scratch

“How do I even start building something in React?”

If you've asked yourself that question, you’re not alone. React can feel intimidating at first, but when you break it down one thought at a time, it becomes a superpower.

Let’s walk through building a To-Do App — not just the code, but the thinking process behind every line. This blog isn’t about copy-pasting; it’s about helping you develop a builder’s mindset.


🧠 Step 1: Think in Components

"What am I building?"

A To-Do App is a perfect starter project because it helps you practice:

  • Displaying a list
  • Adding new items
  • Updating item state (like marking it done)
  • Removing items

So let’s break it into components:

  • App: The heart that holds everything.
  • TodoList: Displays the list of items.
  • TodoItem: Represents one task.

⚙️ Step 2: Set Up the Project

We’re using Vite for a fast and modern React setup.

npm create vite@latest react-todo-app -- --template react cd react-todo-app npm install npm run dev 
Enter fullscreen mode Exit fullscreen mode

Open src/App.jsx — that’s where your app lives.


📦 Step 3: Start from the Middle — The App.jsx
When I build something new, I don’t write everything at once. I start with what I understand best — in this case, managing the list of todos.

Here’s the code (with thought bubbles):

import { useState } from 'react'; import TodoList from './components/TodoList'; function App() { const [todos, setTodos] = useState([]); // 🧠 The to-do list const [text, setText] = useState(''); // ✍️ What the user is typing const addTodo = (e) => { e.preventDefault(); // ⛔ Prevent page refresh when form is submitted if (text.trim()) { const newTodo = { id: Date.now(), // Unique ID using current timestamp text, // The text the user typed completed: false // New tasks are not completed by default }; setTodos([...todos, newTodo]); // ✅ Add the new task to the existing list setText(''); // 🔄 Clear input field } }; // Toggle completion const toggleComplete = (id) => { setTodos(todos.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo )); }; // Delete a task const deleteTodo = (id) => { setTodos(todos.filter(todo => todo.id !== id)); }; return ( <div> <h1>React To-Do App</h1> {/* 🧠 Add a task */} <form onSubmit={addTodo}> <input type="text" value={text} placeholder="Add a new task" onChange={(e) => setText(e.target.value)} /> <button type="submit">Add</button> </form> {/* 🧠 Show the list */} <TodoList todos={todos} onToggle={toggleComplete} onDelete={deleteTodo} /> </div> ); } export default App; 
Enter fullscreen mode Exit fullscreen mode

🔍 What’s happening:

  • The form is submitted → The addTodo function is called.
  • If there's something typed (text.trim()), it creates a new todo object.
  • The new task is added to the existing todos array using the spread operator.
  • setText('') clears the input so the user can type the next task.
  • Every task has a unique ID.
  • In deleteTodo → filter() creates a new array excluding the task with the matching ID.
  • The new filtered array is set back into the state using setTodos().
  • map() goes through all tasks.
  • In toggleComplete → when it finds the one with the matching ID, it flips the value of completed.
  • The ...todo keeps everything else the same, only changing completed.

🔄 Step 4: Build TodoList & TodoItem

Why separate them?

Because every task can be reused, styled, or enhanced on its own. Keeping things modular = clean code.

TodoList.jsx

import TodoItem from './TodoItem'; function TodoList({ todos, onToggle, onDelete }) { return ( <ul> {todos.map(todo => ( <TodoItem key={todo.id} todo={todo} onToggle={onToggle} onDelete={onDelete} /> ))} </ul> ); } export default TodoList; 
Enter fullscreen mode Exit fullscreen mode

TodoItem.jsx

function TodoItem({ todo, onToggle, onDelete }) { return ( <li> <span onClick={() => onToggle(todo.id)} style={{ textDecoration: todo.completed ? 'line-through' : 'none', cursor: 'pointer', marginRight: '10px' }} > {todo.text} </span> <button onClick={() => onDelete(todo.id)}>❌</button> </li> ); } export default TodoItem; 
Enter fullscreen mode Exit fullscreen mode

🧩 Step 5: Test the Flow (and Think Like the User)

  1. Can I add a task?
  2. Can I mark it as done?
  3. Can I delete it?
  4. Does the app feel responsive and logical?

*If yes, congrats! You’ve just built your first real React app *🎉


🌱 Thought-Driven Dev Tips

  1. Don’t start with a blank screen — start with what makes sense to you.
  2. Build piece by piece. Get one feature working before adding the next.
  3. Keep asking: "What should happen when the user clicks here?"
  4. Think in state: “What changes in the app when I do something?”

Top comments (0)