How to create a simple to-do app in React

The rapid evolution of web technologies has reshaped the landscape of digital experiences, demanding developers to adapt and embrace new tools that can translate their creativity into user-friendly applications. One such technology is React, which has emerged as a popular JavaScript library for creating dynamic and interactive user interfaces. We can use it to create phenomenal web applications. Starting with simpler projects first enables us to grasp the fundamentals of web technology more effectively, and building a simple to-do application is a great start.

Building to-do application

Let’s look at how to incrementally create a simple to-do application that implements the CRUDcreate, read, update, and delete operations. Our application will be able to:

  • Add new tasks.

  • Read all the tasks.

  • Edit any of the existing tasks.

  • Delete any of the existing tasks.

Application setup

To begin, we can create a React project using the following command:

npx create-react-app todo-app

This will create a new todo-app named directory in the current working directory. We can navigate into this directory and start working on our React application. There’ll be a variety of files and folders, including the main application file, src/index.js. We can keep the ones that we need and delete the rest. To store the components that we would create for our to-do app, we create a components named directory inside the src directory.

Application structure

A simple React application typically consists of multiple components[object Object] that work together to provide the required functionality. For our to-do app, we’ll use a couple of components along with the main component. These provide the user interface for adding, displaying, and managing tasks.

  • src/App.js: This is the main component of the application and serves as its entry point. It holds the state of the to-do tasks and manages their addition, removal, and update status. Additionally, it renders other components, such as TodoForm and TodoList, to provide a complete user interface for interacting with the app.

  • src/components/ToDoForm.js: This component contains an input field and a button for adding a new task. It handles user input and triggers the addTask function when the form is submitted.

  • src/components/ToDoList.js: This component receives the list of tasks and renders them with buttons to edit or delete them. It handles user interactions for editing or deleting tasks by calling the updateTask() and deleteTask() functions.

Add tasks

Once we have the project set up, we can start with the first functionality of our app, i.e., adding the tasks to our to-do list, which demonstrates the “create” operation of CRUD.

Here’s how to do it:

import React, { useState } from 'react';
import './App.css';
import TodoForm from './components/ToDoForm';
function App() {
const [tasks, setTasks] = useState([]);
const addTask = (task) => {
setTasks([...tasks, task]);
};
return (
<div className="App">
<h1>My To-Do App</h1>
<TodoForm addTask={addTask} />
// Rest of the code implementation
</div>
);
}
export default App;

Code explanation:

In the code widget given above:

  • Line 6: We use React’s useState hook to manage the state of an array variable, tasks, in a functional component. Here, tasks stores the array of all the tasks the user has created using our app, and setTasks() is a function that allows us to update the state of tasks. When we make the setTasks(newValue) function call, React rerenders the component with the new value and tasks is updated to newValue. We’ve initialized tasks as an empty array.

Note: You can learn more about the useState hook in the React hooks introduction - useState answer.

  • Lines 8 –10: addTask() takes a single argument, task, and adds it to the tasks array using the setTasks() function.

  • Line 15: The ToDoForm component is rendered here. We pass addTask to this component, which then triggers it when, for example, a user submits a form to add a new task. Here, the user’s input value becomes the argument that goes into addTask.

Note: The complete code for all the components can be found in the example application given at the end of the Answer.

Display tasks

Because users can now add tasks using our application, we need to add the functionality to read those tasks. This demonstrates the “read” operation of CRUD. The component ToDoList has the implementation of this functionality.

Here’s how to do it:

import './App.css';
import TodoForm from './components/ToDoForm';
import ToDoList from './components/ToDoList';
function App() {
// Some code
return (
<div className="App">
<h1>My To-Do App 📝</h1>
<TodoForm addTask={addTask} />
<ul>
{tasks.map((task, index) => (
<ToDoList
index={index}
task={task}
updateTask={updateTask}
deleteTask={deleteTask}
/>
))}
</ul>
</div>
);
}
export default App;

Code explanation:

  • Line 13: tasks.map iterates over each task we have in our application up till now and renders the ToDoList component for each of them.

  • Line 14–18: The index, task, updateTask, and deleteTask props are used to provide data and functions to each ToDoList component, allowing for dynamic rendering and interaction with the individual tasks.

Edit tasks

Let’s see how we can edit any of the already existing tasks. This functionality represents the “update” operation of CRUD. The function responsible for updating the final state of tasks is in the main component.

Here’s how to do it:

function App() {
// Some code
const updateTask = (index, updatedTask) => {
const newTasks = tasks.map((task, i) =>
i === index ? updatedTask : task
);
setTasks(newTasks);
};
return (
// Some code
);
}
export default App;

Code explanation:

  • Line 4: The updateTask() function takes in two arguments: index (index) of the task being updated and updatedTask, which is the new value we want to replace the existing task with. This value is taken by the user once they enter the new value for an existing task in a form placed in the ToDoList component and is passed to updateTask() function along with the existing task’s index.

  • Lines 5–7: Inside the updateTask function, we iterate over each task in the tasks array and look for the task that has the same index value as index. Once found, we replace its value with updatedTask.

  • Line 8: We use the setTasks() function to update the state of tasks array.

Delete tasks

Let’s look at the “delete” operation of CRUD. To demonstrate this operation, let’s add the functionality of deleting any existing tasks in our app. Again, the function responsible for deleting a task is present in the main component.

Here’s how to do it:

function App() {
// Some code
const deleteTask = (index) => {
const newTasks = tasks.filter((_, i) => i !== index);
setTasks(newTasks);
};
return (
// Some code
);
}
export default App;

Code explanation:

  • Line 4: The deleteTask() function takes a single argument, index, which represents the index of the task in the tasks array that the user wants to delete. This function gets triggered when the user clicks the delete button whose code is present in the ToDoList component.

  • Line 5: We use the filter() method to iterate over each task in the tasks array, and remove the task whose index is being passed as the argument.

  • Line 6: We use setTasks() function to update the state of tasks array. Now, it won’t have the task that was deleted.

Example

Finally, let’s take a look at our to-do app. Click the “Run” button to execute the code. 

That’s it! We’ve successfully created a simple to-do app in React. We can transform this initial project into a stepping stone for creating more complex and impressive web applications, ensuring our skills remain at the cutting edge of this dynamic field.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved