An Easy Beginner-Friendly Todo App Tutorial With React

Todo application tutorial with React feature image

Introduction

Welcome to the sixth project in our Top React JS Projects for Beginners 2023-2024: Learn & Build journey. This will be the second project that has been built in 2024.

This tutorial offers a straightforward guide on how to build an interactive and beginner-friendly todo application with a simple UI/UX. The technologies to build this React JS application include Next.js, TypeScript, and TailWind CSS. The todo application is a practice tool that is used to manage tasks by many people daily. It helps you to remind yourself what needs to be done and encourages people to stay motivated by accomplishing them. Thanks to this simple yet powerful feature, it is the application that a lot of people are interested in to build it themselves (like yourself!).

That being said, let’s get started and I hope you have lots of fun following this tutorial.

Design And Requirements

This section shows the design and requirements to build the todo application.

If you want to build the application on your own without following the guide, you can refer to this section to successfully build the todo application.

NOTE: If you have a different taste of mine in terms of the design, don’t hesitate to play around with it and change them!

Design

The image below is the final outcome of our todo application.

Final outcome of the Todo Application
Final outcome of the Todo Application

Since this tutorial will contain a lot of CSS styling, I can’t give you every single detail of them. However, I will give you the general colors and fonts that I’ve used to build this application.

– App background color: bg-stone-100
– Task card (incomplete) color: bg-white, border-red-200
– Task card (complete) color: bg-green-500, border-red-200
– Add button and text: text-gray-500, FaPlus
– Create button: hover:bg-blue-500, hover:border-blue-500
– Complete button: hover:bg-green-500, hover:border-green-500, FaCheck
– Undo (Revert) button: hover:bg-yellow-500, hover:border-yellow-500, FaUndo

Requirements

These are the requirements of the todo application.

The todo application can:
– Show a new task card when AddTask icon or text is clicked
– Show the newly created task card and AddTask icon and text below the card when the create button is clicked
– Change the color of the card to green and do not allow to type in the card when the complete button is clicked
– Change the color of the card to white and allow to type in the card when the undo (revert) button is clicked

With the design and requirements all laid out, let’s get to the hands-on coding and start building the todo application.

Each step on this tutorial will include corresponding code examples to help you understand better. However, if you decide to just read it through and check out the code I posted on my Github, you can visit this todo application Github repo.

Setting Up Next.js App

The todo application will be based on Next.js just like the previous tutorial (the flashcard application). If you have previously completed the flashcard tutorial, you probably already know how to set up the Next.js application. However, for the new readers who are just starting out Next.js, I will outline the steps below.

Below are the steps to set up the Next.js application.

npx create-next-app@latest

Then, you will see some prompts that ask some questions regarding project configuration. You can accept the default values for all the questions.

The Next.js application creation prompts
The Next.js application creation prompts

Once the process is successfully completed, you should be able to see the project folder structure like the below image.

Project structure after initial creation
Project structure after initial creation

You can now go into the project folder that you just created and start the local Next.js server!

cd todo
npm run dev

If you click on the link (http://localhost:3000) that’s provided or type it in the browser URL, then you should be able to see the bootstrapped Next.js page.

The bootstrapped Next.js main page
The bootstrapped Next.js main page

Create Todo Page

Implement basic todo page

Let’s create a page for todo tasks. We are going to call this page ‘todo‘. This means that we need to create a route that is http://localhost:3000/todo.

Before we start adding contents into the todo page, let’s get rid of the global CSS styling that came with initial Next.js setup. Remove everything in globals.css file except these three lines.

@tailwind base;
@tailwind components;
@tailwind utilities;

This will give us clean state of CSS styling. As a next step, let’s create a folder called todo under the app folder and add a file page.tsx.

Project structure after creating todo folder and page
Project structure after creating todo folder and page

Inside the page.tsx, let’s include some basic content.

Now, if you go to the todo endpoint (/todo) in browser URL, then you should be able to see the welcome message displayed.

Welcome message of the Todo application
Welcome message of the Todo application

Since the todo folder we added has the page.tsx, we have the /todo endpoint. As we pointed out in the previous Flashcard tutorial, this is the Next.js’ feature.

Refactor welcome message to header component

The applications from the previous tutorials had the welcome message in a main page as a component. From the current tutorial, we are going to take advantage of Next.js. Next.js has a file called layout.tsx. You might have noticed this file along with the page.tsx that has been created in the beginning. As the name suggests, the layout.tsx file is how the page is going to be laid out. Here is the detailed explanation from the Next.js official documentation for layouts.

Let’s create a folder components under the app folder. Then, create a file Header.tsx under the components folder.

Project structure after creating components folder and Header.tsx
Project structure after creating components folder and Header.tsx

Then, let’s move the welcome message to Header.tsx.

Once we have the Header.tsx component, we can apply this to global layout, which is layout.tsx file under the app folder.

Finally, we can remove the welcome message from the page.tsx under todo folder.

Perfect, you should be able to see the same welcome message as above.

Set todo page as main page

Our todo application doesn’t require users to sign in to use the service. So, this means that we can set the /todo endpoint as our main page. This can be achieved by adding a simple line of code in our main page.

Add this line below to the page.tsx under app folder.

Now, when you access http://localhost:3000, it will automatically redirect to http://localhost:3000/todo.

Create Add Task Button

Implement the Add Task icon and text

Let’s start with building a button that would make us be able to add a new task. This will contain a plus (+) icon as well as ‘Add Task‘ text. Create a file AddButton.tsx under the todo folder.

In order to add a plus icon, we need to first install react-icons package.

npm install react-icons --save

Then, we can use this AddButton component in our page.

If we go back to the browser, we should be able to see something like this:

The Add Task button
The Add Task button

Refactor the code into ToDo component

Our code is successfully working, but I’d like to have a better separation by abstracting some HTML tags into components.

Let’s create a file ToDo.tsx under the todo folder. After creating the file, we can move the code that we used in page.tsx.

Then, we can simply use ToDo component in page.tsx.

Awesome! Personally, I find this to offer improved semantics, and it will facilitate other developers in swiftly engaging with the code.

Create Task Card

Implement Card component

The Card component would be the main component in our todo application. This component will hold a space (textarea) for user input and a button (add, complete, or undo depending on the status of the task). Create a file Card.tsx under the components folder.

Project structure after creating Card.tsx
Project structure after creating Card.tsx

Here is the code for the Card.tsx for now. This will be modified as the tutorial progresses.

We can use this Card component in our ToDo component.

Now our application would look like this.

Rendered Card component on page
Rendered Card component on page

Don’t worry about the Card component and the AddButton component showing right next to each other. We are going to make a change so that we can only see either one at a time.

Implement NewTask component

Like we mentioned above, we are going to show either the new task or the add task button. When we click the AddTask button, it will disappear and show the new task card. When we click the create button (which we haven’t implemented yet) in the new task, it will register the new task as our task and show the AddTask button again below the new task.

Let’s go ahead and create a file NewTask.tsx under todo folder.

Project structure after creating NewTask.tsx
Project structure after creating NewTask.tsx

We can add content this in NewTask.tsx for now. This should let you click on Add Task button and open up a new task card.

We can use NewTask component in the ToDo component.

Implement Create button

Since we are now able to open up a new task card, we need to be able to add this task to our task list by clicking create button on the task card. First, let’s implement the create button inside the task card. Create a file CreateButton.tsx under the todo folder.

Project structure after creating CreateButton.tsx
Project structure after creating CreateButton.tsx

Then the code below is what I wrote for the CreateButton.tsx.

We can use the CreateButton component inside the Card component.

We also need to modify the NewTask component as well because we are maintaining isNewTaskClicked state from the NewTask. That’s why we are passing down handleOnTaskClicked handler to the CreateButton component.

If you click on AddTask button, then it should switch to NewTask component with the CreateButton component. The new task card with the CreateButton component would look like this.

Rendered NewTask component on page
Rendered NewTask component on page

Excellent! Good job on following through the tutorial. At the moment, all it’s doing is toggling between AddTask button and the NewTask component. Our next goal is to add a new task into the list of tasks when the create button is clicked.

You might have noticed that we have some duplicate codes for the buttons. So, before we start implementing the adding a task into the list feature, let’s refactor our code.

Refactor the code for buttons

In our todo application, we are using two types of buttons. One button type contains only an icon, while the other type consists solely of text. Let’s take care of the buttons that only have an icon. Create a file called IconButton.tsx under the components folder.

Project structure after creating IconButton.tsx
Project structure after creating IconButton.tsx

Then, we can implement IconButton.tsx.

We are using an icon only inside the AddButton.tsx. We can replace the button with the icon with IconButton component now.

Next, we also have a button that has text only, which is a create button. We are going to create a file TextButton.tsx under the components folder.

Project structure after creating TextButton.tsx
Project structure after creating TextButton.tsx

Here is the code for the TextButton component. It is very similar to IconButton component but slightly different.

The CreateButton component only has the text inside. So, we can replace button element implementation to TextButton component.

Perfect! Now, go back to your application and test to see if everything is working intended. It’s essential to run some tests after each refactoring. In our case, since we are not writing any test code, we can manually check whether the application is running correctly.

Implement The Functionalities

Until this point of the tutorial we basically created most of components that the application needs. Now the fun parts begin – interactivity!

Add a new task into the task list

The CreateButton that we have now doesn’t actually add a new task into the task list. It just toggles the AddButton component back to the browser. But this is not exactly what we want, is it? We want to add this new task to the task list. In order for this to work, we need to have a state that stores tasks. The ToDo component will be the right place to have this.

Next, we are going to have a state for the new task in the NewTask component.

Since the function handleCreateButtonClick is being passed down as a prop handleCreateTaskOnClick, we need to modify the Card component as well.

Go back to the browser and then test the application out. If you click the create button, it should be adding a task into the task list.

Added task by clicking Create button
Added task by clicking Create button

Update the new task content

Although we are able to add a new task into the task list, the task doesn’t have any content saved. This is because we haven’t implemented saving the content of the new task.

The state newTask resides in the NewTask component. So, we need to implement a handler here and pass it down to the Card component.

Now, when you input some texts in the user input area and hit the create button, we can see the content actually is showing for each task.

Changed task content with onChange handler
Changed task content with onChange handler

Show the existing tasks

Now that the tasks inside the task list have contents , let’s proceed to display them. To achieve this, we’ll need to make some adjustments to the ToDo and Card components.

Let’s start with the ToDo component.

Then, we can use content prop in the Card component.

NOTE: I’ve passed some other things as props along with content. This is because they are going to be used soon for other functionalities.


If you go back to the browser and add new tasks, they will show up with the content you typed in.

Successfully rendered existing tasks
Successfully rendered existing tasks

Awesome! But you might have noticed that after the existing tasks show up, if you try to modify them, the content won’t change. This is what we are going to take care of next.

Modify the existing tasks

Currently, the Card component is being used in two places. One place is the ToDo component and another one is the NewTask component. When the content of the card in the NewTask is being updated, what’s really being updated is the newTask state. If we want to modify the existing card, we would have to modify the tasks state which is in the ToDo component.

So, let’s go ahead and implement a function that can take care of both cases in the Card component. You can think of this function as a controller that decides which function to call.

If you look at what’s inside the handleOnChange function, you can see we are calling two different functions: handleNewTaskContentChange and handleContentChange. We already have the handleNewTaskContentChange function but there is a small change we need to make in the NewTask component. Since we need to distinguish whether the card is new or not, we need to pass isNew flag into the Card component in the NewTask component.

Next, we need to implement handleContentChange function in the ToDo component.

Now, you should be able to modify the existing tasks as well as the new task card. 🙂

Successfully modified existing tasks content
Successfully modified existing tasks content

Implement Complete And Undo Button

Implement the complete button

As a task management application, users should have the ability to mark tasks as completed or mark them as incomplete. At the moment, if you create a task, you will still see the complete button on the task card. We are going to change this to an icon button that has a check mark with the green background.

Let’s create a file CompleteButton.tsx under the todo folder.

Project structure after creating CompleteButton.tsx
Project structure after creating CompleteButton.tsx

Then we can implement the CompleteButton component like below.

The CompleteButton component will reside in the Card component. So, we would need to make a change to the Card component as well.

Let me explain what have changed here. For now, we are conditionally rendering the CreateButton and the CompleteButton based on the isNew. Later on, we are going to add one more condition to render UndoButton. We’ve also added the handleCompleteOnClick function which is calling the handleCompleteChange function that is passed as a prop. The reason that this function is being passed a prop is because that we are modifying the complete field in each task. The tasks are managed in the ToDo component.

Depending on the complete prop, we are changing the styles of the card. On top of that, we are making the card as read-only mode if the task is completed.

Now then, let’s modify the last component: ToDo.

If you click on the complete button, then you should be able to see the task changed.

Successfully rendered completed task
Successfully rendered completed task

Implement the undo button

Occasionally, situations arise where we need to undo a completed task. This could be due to accidental clicks on the completion button or because a task was mistakenly believed to be finished when it wasn’t. 🙁

Let’s create a file called UndoButton.tsx under the todo folder.

Project structure after creating UndoButton.tsx
Project structure after creating UndoButton.tsx

The UndoButton component will be the exactly same thing as the CompleteButton but with a different icon and color.

Now, we can add one more condition to render the UndoButton component inside the Card component.

You should be able to undo the completed task after clicking the undo button.

Successfully rendered UndoButton
Successfully rendered UndoButton

Conclusion

Excellent job on making it through until the end! Now you have the fully working todo application on your hand. This is the end of our sixth React JS step-by-step tutorial.

Photo by Massimo Virgilio on Unsplash
Photo by Massimo Virgilio on Unsplash

From this tutorial, we were able to learn the following:
– How to set up React JS application featured by Next.js
– How to create buttons that contain icon or text only
– How to conditionally render components
– How to update props being passed down to children components
– How to utilize the handler functions being passed down to children components
– How to create re-usable components by refactoring

If you’re planning to improve your skills further, you’re welcome to enhance the Todo application by adding more features on to it. Here are some ideas that I can think of to improve:
– Add a strike-through when the task is completed
– Add a separate section that has completed tasks only
– Add a cool animation on task completion or un-doing
– Add a functionality that blocks creating tasks with empty content

I hope you enjoyed building the Todo application and thank you again for reading. If you are interested in making more React JS projects for beginners, then you can check out our Top React JS Projects for Beginners 2023-2024: Learn & Build post.

If you have thoughts or any feedbacks on the post, be sure to leave a comment.

I also write articles on Medium, so make sure to check them out on medium/@codingnotes.

Happy coding!

2 thoughts on “An Easy Beginner-Friendly Todo App Tutorial With React”

Leave a Comment

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