In this post let us see how we can easily build a task management app (which is totally & completely different from a todo app) using ReactJS. We will be using React Hooks, Chota CSS for styling and a lot of ES6+. We will not look at any centralised state management, or deal with a backend to store the tasks in this post.
Get Started
Use create-react-app to structure your project like any sane person would do -
|
|
Proceed to have a dozen cups of coffee while your app gets initialised. Open the project root folder in VSCode to see this beautiful structure.

Start your app..
|
|
Navigate to http://localhost:3000 in your browser to see the app.
While the create-react-app seems to do a lot of things (it does), you only need to care about a few things at this time -
- The app gets anchored with one HTML file
public/index.htmland in a single node<div id="root"></div>(for the most part, bear with me). The file itself will not have direct reference to the javascript - that will be done through a build step src/index.jsrefers to therootelement1 2 3 4 5 6ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById("root") );Appinindex.jsis where you will start coding React magic. You shall use JSX in the React components to do all that magic, or you shall perish -1 2 3 4 5 6 7function App() { return ( <div className="App"> <header className="App-header"></header> </div> ); }
Read more about JSX here, or just follow along by keeping mind two things -
- You can code all the HTML you want within JSX
- Use
classNameinstead ofclassto reference css classes - Use
{ }notation within JSX for expressions, variables etc.
Let’s do a quick take on how the components work. Create a new file src/components/HelloWorld.js -
|
|
JSX can have only one root - <></> tags take care of that. Rest is just plain HTML.
Include HelloWorld in App.js -
|
|
You can see the bold words in your app almost immediately.
Before we proceed, install a few things to make your React development easier -
- Install React Developer Tools. This will add a couple of tabs in your Chrome Developer Tools to view components. By default you should see
ComponentsandProfiler - Install VSCode extension ES7 React/Redux/GraphQL/React-Native snippets that will give you a bunch of useful snippets
You can use Emmet scripts to quickly create boilerplate and scripts - for e.g. create a new file and type in rfce and tab to see it in action.
Let us also include some styles, because well, we’re not cave-men.
Include the below line in your index.html to include a small, class-light library called chota.css -
|
|
Create a new file src/assets/styles.css and leave it blank for now. We will use it for custom styling. Include the file in App.js -
|
|
I will not really touch upon this topic again - but you can see the CSS code in the Github repo.
Creating Structure for your App
Let us create the basic elements for our app. First, the header - create a new file called src/components/Header.js.
|
|
We have seen this earlier - JSX and stuff. The only difference is the CSS classes. Let’s include this Header in App.js.
|
|
The above changes should give you a good idea about where we are going. We create a Header component, and include it in the main App so that it’s available everywhere in the app.
We can now include the main content just below Header.
Create Tasks Component
The simplest way to create a task component is familiar to us by now. Create a new file src/components/Tasks.js -
|
|
Include Tasks in App.
|
|
You should now be able to see the tasks and a button “New” that doesn’t do anything.
Let’s level up. We will create a state variable tasks that will store tasks and can be accessible from different components. We could eventually use this variable to store tasks retrieved from a database or a file.
In the simplest design we could create tasks in Tasks component, but that would make it difficult to access in a different component. For example, we need tasks in both Tasks, which is a list of tasks, and TaskDetail, which shows the detail task and allows user to edit tasks. So, let’s create tasks in App.js for now.
|
|
We have done a couple of cool things here -
- Import
useStatefrom React - Create
tasksand initialize it.tasksis the variable name andsetTasksis used to set this variable with a value. We will not be usingsetTasksright now, but directly provide the initial value as a param touseState1 2 3 4const [tasks, setTasks] = useState([ { desc: "Learn React", id: 1 }, { desc: "Profit", id: 2 }, ]); - Pass
tasksas props to theTaskscomponent
Let us receive tasks prop in our Tasks component.
|
|
As you can see -
- We receive a destructured
tasksprop in the function. You could also define param as simplyprops(no brackets, no destructuring), and refer to tasks asprops.tasks - We cycle through
tasksand display individual task usingh3element. The brackets contain the overall expression in JSX1 2 3 4 5 6 7 8 9{ tasks.map((task) => { return ( <div className="col-12 text-left" key={task.id}> <h4>{task.desc}</h4> </div> ); }); }
You should now see..
Create Task Component
Tasks component is doing too many things -
- List tasks
- Show task
- er.. that’s about it, but it has the potential to much more
Let’s break the component so that display of a task becomes concern of a different component.
Create a new file src/components/Task.js -
|
|
The code is a direct copy of the fragment responsible to display a specific task in Tasks component.
Include Task in Tasks -
|
|
This is very similar to last time when we included one component in another. We are just using an additional attribute called key, which denotes the unique identifier of each element in the array (and that will be task.id in our case).
The added advantage of segregating task display is that we can use this Task component to display individual task anywhere in the application.
Fantastic.
Let us add more attributes to the task and beautify this a bit. Afterall, a task is not simply about the description.
Add more attributes to the task in App.
|
|
Change Task.js to make tasks come alive -
|
|
And, voila..

Add Functionality: Complete Task
We have successfully displayed a task so far, but we don’t have any functionality enabled on the task. We will change that by inserting a button to toggle the task status.
Add a function that can get called to change status. If we had a global state (e.g. Redux), we would have more independence to decide where this function has to reside. Since we are using a simple state at App level, we will introduce the function to change that state also within App. Create a new function -
|
|
The child component (Tasks in our case) does not know about this method. We have to pass the function to the inner-most component that will call it for some greater good.
Change App.js to pass onTglStatus as a prop -
|
|
Repeat the above code in Tasks.js -
|
|
Call the function in Task.js -
|
|
Click the button to see the debug statement in developer console.
We have moved props including variables(state) and functions that act on the state down the chain, and events get surfaced up the component chain!
Add the function to do something beyond just a debug statement in App.js -
|
|
In the above code we use Array.map function to change status for the matching record. The matching record (task) is supplied by the function called by button click event.
Let us change the checkbox/button in indicate completed tasks in Task.js.
|
|
We use one of the old Javascript tricks to display ✅ when task is complete.
{task.complete && "✅"}evaluates to✅whentask.completeis true. Else this returns nothing- The next expression
{!task.complete && "⬜"}returns⬜whentask.completeis false
Since our application is reactive to changes and the button label gets driven by data, we see the below behaviour.

Add Functionality: Add Task
We need to get task data from user and save new tasks. To that end, add the function to save tasks onSaveTask in App.js -
|
|
We are using setTasks like before, but this time we are adding a new element to the array. Again, we don’t add the element directly to array since the state variable is immutable. We rather do this -
- create a new variable
- Add
{ desc: desc, date: date, id: Date.now(), complete: false },as the first element.descanddateare passed along by the form used to create new tasks.Date.now()is a simple way to make the id unique - Add the existing array elements to the new array variable
...tasks
Next, add a new form to collect data. Let’s create a new component src/components/TaskEdit.js. This component will receive the function onSaveTask as props and call it on button click.
|
|
We have gone through some of the functions implemented here, while rest are mostly HTML -
const [desc, setDesc] = useState("");creates a new state variale local toTaskEditand initiates it to""- a standard HTML form displays a couple of text boxes to collect
descanddate Savebutton calls a local methodsaveTask, which will call theonSaveTaskfunction inApp.jsand resets form
Add functionality to New button so that it displays the TaskEdit component and associated form (and can also toggle to hide the form).
In App.js -
|
|
We conditionally show TaskEdit only if showTaskEdit is true.
Time to see the complete application in action -

Next Steps
- Enable user to edit tasks to change description and dates
- Add a backend for your task manager
Code for this project is available in the Github repo.