Task Manager App using MERN Stack
Last Updated :
24 Apr, 2025
Task Manager is very crucial to manage your tasks. In this article, we are going to develop a task manager application using the MERN stack. This application helps users to manage their tasks efficiently, offering essential features like creating new tasks, editing existing ones, and deleting tasks as needed. We'll walk through the step-by-step process of creating this application.
Output Preview: Let us have a look at how the final output will look like.
Preview of Final OutputPrerequisites:
Approach to create Task Manager App:
- Determine the features and functionalities required for the task manager application, such as task creation, editing, deletion, and viewing tasks.
- Choose and install the required dependencies and requirements which are more suitable for the Project.
- Create the folder structure and components of the project.
- Design and Implement the Frontend of the project.
- Create a Backend Server as well as design and implement the APIs required for the project development.
- Integrate the Backend with the Frontend and test it, either manually or using some testing library.
Steps to Create the Frontend:
Step 1: Set up React frontend and get into it using the command
npx create-react-app client
cd client
Step 2: Install the required dependencies(axios, tailwindcss).
npm install axios
npm install -D tailwindcss
npx tailwindcss init
Step 3: Configure the tailwind.config.js file
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js,jsx}"],
theme: {
extend: {},
},
plugins: [],
}
Step 4: Add the Tailwind directives to your CSS in index.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Step 5: Start Tailwind CLI
npx tailwindcss -i ./src/index.css -o ./src/output.css --watch
Project Structure:
Frontend Folder StructureThe updated dependencies in package.json file will look like:
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"devDependencies": {
"tailwindcss": "^3.4.1"
}
Example Code: Create the required files and write the following code.
JavaScript
//src/Components/Filterbar.jsx
import React from 'react';
import { useTaskContext } from '../Context/TaskContext';
function Filterbar() {
const { handleFilterClick } = useTaskContext();
return (
<div className="flex justify-center mt-8">
<button
className="filter-button bg-blue-500
hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-l"
onClick={() => handleFilterClick('all')}
>
All
</button>
<button
className="filter-button bg-blue-500 hover:bg-blue-600
text-white font-bold py-2 px-4"
onClick={() => handleFilterClick('completed')}
>
Completed
</button>
<button
className="filter-button bg-blue-500 hover:bg-blue-600
text-white font-bold py-2 px-4 rounded-r"
onClick={() => handleFilterClick('todo')}
>
To Do
</button>
</div>
);
}
export default Filterbar;
JavaScript
//src/Components/Navbar.jsx
import React, { useState } from 'react';
import AddTaskModal from '../Modals/AddTaskModal';
function Navbar() {
const [isModalOpen, setIsModalOpen] = useState(false);
const openModal = () => {
setIsModalOpen(true);
};
const closeModal = () => {
setIsModalOpen(false);
};
return (
<nav className="bg-gray-800 py-4">
<div className="max-w-7xl mx-auto px-4 flex
justify-between items-center">
<div>
<span className="text-white text-lg
font-bold">Task Manager</span>
</div>
<div>
<button className="bg-blue-500 hover:bg-blue-600
text-white font-bold py-2 px-4 rounded"
onClick={openModal}>
Add
</button>
</div>
</div>
<AddTaskModal isOpen={isModalOpen} closeModal={closeModal} />
</nav>
);
}
export default Navbar;
JavaScript
//src/Components/TaskList.jsx
import React, { useState } from 'react';
import { useTaskContext } from '../Context/TaskContext';
import DeleteModal from '../Modals/DeleteModal';
import EditModal from '../Modals/EditModal';
function TaskList() {
const { filteredTasks, updateTaskStatus } = useTaskContext();
const [taskId, setTaskId] = useState('');
const [taskTitle, setTaskTitle] = useState('');
const [taskDescription, setTaskDescription] = useState('');
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [openDropdownId, setOpenDropdownId] = useState(null);
const handleDelete = (taskId) => {
setTaskId(taskId);
setIsDeleteModalOpen(true);
setOpenDropdownId(null);
};
const handleEdit = (taskId, taskTitle, taskDescription) => {
setTaskId(taskId);
setTaskTitle(taskTitle);
setTaskDescription(taskDescription);
setIsEditModalOpen(true);
setOpenDropdownId(null);
};
const handleComplete = (taskId) => {
updateTaskStatus(taskId, 'completed');
setOpenDropdownId(null);
};
const toggleDropdown = (taskId) => {
setOpenDropdownId(openDropdownId === taskId ? null : taskId);
};
const isDropdownOpen = (taskId) => {
return openDropdownId === taskId;
};
const getStatusColor = (status) => {
switch (status) {
case 'todo':
return 'bg-yellow-200';
case 'completed':
return 'bg-green-200';
default:
return 'bg-gray-200';
}
};
return (
<div className="my-8 grid gap-4 grid-cols-1 sm:grid-cols-2
md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
{filteredTasks.map(task => (
<div key={task._id} className=
{`relative rounded-md shadow-md
${getStatusColor(task.status)}`}>
<div className="p-4">
<div className="flex justify-between items-center mb-2">
<h3 className="text-lg font-semibold">
{task.title}
</h3>
<button onClick={() => toggleDropdown(task._id)}
className="text-gray-500 hover:text-gray-700">
<svg className="w-6 h-6" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
<p className="text-sm text-gray-600 mb-4">
{task.description}</p>
</div>
{isDropdownOpen(task._id) && (
<div className="absolute top-full right-0 mt-2 w-48
bg-white border rounded-md shadow-md z-10">
<button className="block w-full py-2 px-4
text-left hover:bg-gray-100" onClick={() =>
handleEdit(task._id, task.title,
task.description)}>
Edit</button>
<button className="block w-full py-2 px-4
text-left text-red-600 hover:bg-red-100"
onClick={() => handleDelete(task._id)}>
Delete</button>
{task.status !== 'completed' && (
<button className="block w-full py-2 px-4
text-left hover:bg-gray-100"
onClick={() =>
handleComplete(task._id)}>
Mark as Completed
</button>
)}
</div>
)}
</div>
))}
<DeleteModal isOpen={isDeleteModalOpen}
closeModal={() => setIsDeleteModalOpen(false)}
taskId={taskId} />
<EditModal isOpen={isEditModalOpen}
closeModal={() => setIsEditModalOpen(false)} taskId={taskId}
initialTitle={taskTitle} initialDescription={taskDescription} />
</div>
);
}
export default TaskList;
JavaScript
//src/Context/TaskContext.js
import React, {
createContext,
useContext,
useEffect,
useState
} from "react";
import axios from "axios";
const TaskContext = createContext();
const apiUrl = process.env.REACT_APP_API_URL || "http://localhost:5000";
export const useTaskContext = () => {
return useContext(TaskContext);
};
export const TaskProvider = ({ children }) => {
const [tasks, setTasks] = useState([]);
const [filteredTasks, setFilteredTasks] = useState([]);
const [totalTasks, setTotalTasks] = useState(0);
const [completedTasks, setCompletedTasks] = useState(0);
const [todoTasks, setTodoTasks] = useState(0);
useEffect(() => {
fetchData();
}, [totalTasks]);
const fetchData = async () => {
try {
const response = await axios.get(`${apiUrl}/tasks`);
setTasks(response.data);
setFilteredTasks(response.data);
setTotalTasks(response.data.length);
const completedCount = response.data.filter(
(task) => task.status === "completed"
).length;
setCompletedTasks(completedCount);
setTodoTasks(response.data.length - completedCount);
} catch (err) {
console.error("Error fetching data:", err);
}
};
const handleFilterClick = (status) => {
if (status === "all") {
setFilteredTasks(tasks);
} else {
const filtered = tasks.filter((task) =>
task.status === status);
setFilteredTasks(filtered);
}
};
const addTask = async (title, description, status) => {
try {
const response = await axios.post(`${apiUrl}/tasks`, {
title,
description,
status,
});
setTasks([...tasks, response.data]);
if (status === "completed") {
setCompletedTasks((prev) => prev + 1);
} else {
setTodoTasks((prev) => prev + 1);
}
setTotalTasks((prev) => prev + 1);
} catch (err) {
console.error("Error adding task:", err);
}
};
const deleteTask = async (taskId) => {
try {
await axios.delete(`${apiUrl}/tasks/${taskId}`);
const updatedTasks = tasks.filter((task) => task.id !== taskId);
setTasks(updatedTasks);
setFilteredTasks(updatedTasks);
setTotalTasks((prev) => prev - 1);
const completedCount = updatedTasks.filter(
(task) => task.status === "completed"
).length;
setCompletedTasks(completedCount);
setTodoTasks(updatedTasks.length - completedCount);
} catch (err) {
console.error("Error deleting task:", err);
}
};
const editTask = async (
taskId,
updatedTitle,
updatedDescription,
updatedStatus
) => {
try {
await axios.put(`${apiUrl}/tasks/${taskId}`, {
title: updatedTitle,
description: updatedDescription,
status: updatedStatus,
});
fetchData();
} catch (err) {
console.error("Error editing task:", err);
}
};
const updateTaskStatus = async (taskId, status) => {
try {
await axios.put(`${apiUrl}/tasks/${taskId}`, { status });
const updatedTasks = tasks.map((task) =>
task._id === taskId ? { ...task, status } : task
);
setTasks(updatedTasks);
setFilteredTasks(updatedTasks);
setCompletedTasks((prev) =>
status === "completed" ? prev + 1 : prev - 1
);
setTodoTasks((prev) => (status !== "completed" ?
prev + 1 : prev - 1));
} catch (err) {
console.error("Error updating task status:", err);
}
};
return (
<TaskContext.Provider
value={{
filteredTasks,
totalTasks,
completedTasks,
todoTasks,
handleFilterClick,
addTask,
deleteTask,
editTask,
updateTaskStatus,
}}
>
{children}
</TaskContext.Provider>
);
};
JavaScript
//src/Modals/AddTaskModal.jsx
import React, { useState } from 'react';
import { useTaskContext } from '../Context/TaskContext';
function AddTaskModal({ isOpen, closeModal }) {
const { addTask } = useTaskContext();
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [status, setStatus] = useState('todo');
const handleSubmit = () => {
addTask(title, description, status);
setTitle('');
setDescription('');
setStatus('todo');
closeModal();
};
return (
<div className={`modal ${isOpen ? 'block' : 'hidden'}
fixed inset-0 z-10 overflow-y-auto`}
style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }}>
<div className="modal-container bg-white
w-full md:w-1/3 mx-auto mt-20 p-6 rounded shadow-lg">
<div className="modal-header flex justify-between items-center">
<h3 className="text-lg font-semibold">Add New Task</h3>
<button className="text-gray-500 hover:text-gray-800"
onClick={closeModal}>X</button>
</div>
<div className="modal-body mt-4">
<div className="mb-4">
<label className="block text-gray-700 text-sm
font-bold mb-2" htmlFor="title">Title</label>
<input className="border rounded w-full py-2
px-3 text-gray-700
leading-tight focus:outline-none
focus:shadow-outline"
id="title" type="text" value={title}
onChange={(e) => setTitle(e.target.value)} />
</div>
<div className="mb-4">
<label className="block text-gray-700 text-sm
font-bold mb-2"
htmlFor="description">
Description
</label>
<input className="border rounded w-full
py-2 px-3 text-gray-700
leading-tight focus:outline-none
focus:shadow-outline"
id="description"
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}/>
</div>
<button className="bg-blue-500 hover:bg-blue-600
text-white font-bold
py-2 px-4 rounded"
onClick={handleSubmit}>
Add Task
</button>
</div>
</div>
</div>
);
}
export default AddTaskModal;
JavaScript
//src/Modals/DeleteModal.jsx
import React from 'react';
import { useTaskContext } from '../Context/TaskContext';
function DeleteModal({ isOpen, closeModal, taskId }) {
const { deleteTask } = useTaskContext();
const handleDelete = () => {
deleteTask(taskId);
closeModal();
};
return (
<div className={`modal ${isOpen ? 'block' : 'hidden'}
fixed inset-0 z-10 overflow-y-auto`}
style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }}>
<div className="modal-container bg-white w-full
md:w-1/3 mx-auto mt-20 p-6 rounded shadow-lg">
<div className="modal-header flex justify-between
items-center">
<h3 className="text-lg font-semibold">Confirm Delete</h3>
<button className="text-gray-500 hover:text-gray-800"
onClick={closeModal}>X</button>
</div>
<div className="modal-body mt-4">
<p>Are you sure you want to delete this task?</p>
<div className="flex justify-end mt-4">
<button className="bg-red-500 hover:bg-red-600
text-white font-bold py-2 px-4 rounded mr-2"
onClick={handleDelete}>Delete</button>
<button className="bg-gray-300 hover:bg-gray-400
text-gray-800 font-bold py-2 px-4 rounded"
onClick={closeModal}>Cancel</button>
</div>
</div>
</div>
</div>
);
}
export default DeleteModal;
JavaScript
//src/Modals/EditModal.jsx
import React, { useState } from 'react';
import { useTaskContext } from '../Context/TaskContext';
function EditModal({ isOpen, closeModal, taskId, initialTitle = '' }) {
const { editTask } = useTaskContext();
const [title, setTitle] = useState(initialTitle);
const handleSubmit = () => {
editTask(taskId, title);
closeModal();
};
return (
<div className={`modal ${isOpen ? 'block' : 'hidden'}
fixed inset-0 z-10 overflow-y-auto`}
style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }}>
<div className="modal-container bg-white w-full
md:w-1/3 mx-auto mt-20 p-6 rounded shadow-lg">
<div className="modal-header flex justify-between items-center">
<h3 className="text-lg font-semibold">Edit Task</h3>
<button className="text-gray-500 hover:text-gray-800"
onClick={closeModal}>X</button>
</div>
<div className="modal-body mt-4">
<div className="mb-4">
<label className="block text-gray-700
text-sm font-bold mb-2" htmlFor="title">Title</label>
<input className="border rounded w-full py-2
px-3 text-gray-700
leading-tight focus:outline-none
focus:shadow-outline"
id="title"
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)} />
</div>
<div className="flex justify-end mt-4">
<button className="bg-blue-500 hover:bg-blue-600
text-white font-bold py-2 px-4 rounded mr-2"
onClick={handleSubmit}>Save</button>
<button className="bg-gray-300 hover:bg-gray-400
text-gray-800 font-bold
py-2 px-4 rounded"
onClick={closeModal}>Cancel</button>
</div>
</div>
</div>
</div>
);
}
export default EditModal;
JavaScript
//src/App.js
import './App.css';
import Filterbar from './Components/Filterbar';
import Navbar from './Components/Navbar';
import { TaskProvider } from './Context/TaskContext';
import Tasks from "./Components/TaskList"
function App() {
return (
<>
<TaskProvider>
<Navbar />
<Filterbar />
<Tasks />
</TaskProvider>
</>
);
}
export default App;
To Start the frontend run the following command:
npm start
Steps to Create the Backend:
Step 1: Create a directory for project
mkdir server
cd server
Step 2: Initialized the Express app and installing the required packages
npm init -y
npm i express mongoose cors dotenv
Project Structure:
Backend Folder StructureThe updated dependencies in package.json file will look like:
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"mongoose": "^8.2.0"
}
Example: Create 'server.js' and write the below code.
JavaScript
//server.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
mongoose.connect(process.env.MONGODB_URI)
.then(() => console.log("Database Connected!"))
.catch(err => console.error("Database connection error:", err));
const taskSchema = new mongoose.Schema({
title: String,
description: String,
status: String
});
const Task = mongoose.model('Task', taskSchema);
async function getTask(req, res, next) {
try {
const task = await Task.findById(req.params.id);
if (!task) {
return res.status(404).json({ message: 'Task not found' });
}
res.task = task;
next();
} catch (err) {
return res.status(500).json({ message: err.message });
}
}
app.get('/tasks', async (req, res) => {
try {
const tasks = await Task.find();
res.json(tasks);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
app.post('/tasks', async (req, res) => {
const task = new Task({
title: req.body.title,
description: req.body.description,
status: req.body.status
});
try {
const newTask = await task.save();
res.status(201).json(newTask);
} catch (err) {
res.status(400).json({ message: err.message });
}
});
app.put('/tasks/:id', getTask, async (req, res) => {
if (req.body.title != null) {
res.task.title = req.body.title;
}
if (req.body.description != null) {
res.task.description = req.body.description;
}
if (req.body.status != null) {
res.task.status = req.body.status;
}
try {
const updatedTask = await res.task.save();
res.json(updatedTask);
} catch (err) {
res.status(400).json({ message: err.message });
}
});
app.delete('/tasks/:id', getTask, async (req, res) => {
try {
await res.task.deleteOne();
res.json({ message: 'Task deleted' });
} catch (err) {
res.status(500).json({ message: err.message });
}
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
To start the application run the following command:
node server.js
Output:
Output of data saved in Database:
MongoDB Data StoreBrowser Output:
Final Output
Similar Reads
Notes Maker App using MERN Stack
The "Notes Maker" project is an helpful web application designed to help users effectively create, manage, and organize their notes. In this article we are utilizing the MERN (MongoDB, Express, React, Node) Stack, to build a notes maker application that provides a seamless user experience for note-t
6 min read
Task Management App Using TypeScript
Task management apps help users organize their daily activities effectively. In this article, we will create a simple task management app that allows users to add and delete tasks. Weâll use HTML for structure, CSS for styling, and TypeScript for functionality to make it robust and type-safe. What W
7 min read
Notes Maker App using MEAN Stack
The Notes Maker project is a helpful web application designed to help users effectively create, manage, and organize their notes. In this article we are utilizing the MEAN (MongoDB, Express, Angular, Node) Stack, to build a notes maker application that provides a seamless user experience for note-ta
7 min read
Restaurant App using MERN Stack
Creating a Restaurant app will cover a lot of features of the MERN stack. In this tutorial, we'll guide you through the process of creating a restaurant application using the MERN stack. The application will allow users to browse through a list of restaurants, view their menus, and add items to a sh
11 min read
Expense Management System using MERN Stack
In this article, weâll walk through the step-by-step process of creating a Expense Management System using the MERN (MongoDB, ExpressJS, React, NodeJS) stack. This project will showcase how to set up a full-stack web application where users can add their budget and put daily expenses that get deduct
14 min read
Social Fitness App using MERN Stack
Creating a Social Fitness App is a great opportunity for anyone who wants to understand full-stack development. In this article, we'll make a Social Fitness App from scratch using the MERN(MongoDB, Express.js, React, Node.js) stack. This project will help navigate backend development, and teach you
9 min read
Create a Task Manager App using React-Native
In this article, we'll walk you through the process of building a basic Task Manager app using React Native. The application enables users to effortlessly create, edit, complete/incomplete, and delete tasks, providing an uncomplicated yet impactful introduction to ReÂact Native's mobile app develop
7 min read
Build a Task Management App using Next JS
A Task management app is a useful web application that assists in efficiently organizing and managing tasks. It provides various functionalities such as creating tasks, assigning prioritieÂs and deadlines, marking complete tasks, and enabling task search based on keÂywords and priorities. Preview of
5 min read
Quiz App using MERN Stack
In this article, weâll walk through the step-by-step process of creating a complete quiz application with MongoDB, ReactJS, ExpressJS, and NodeJS. This application will provide users with a user-friendly interface for taking and submitting quizzes and a scoreboard to check their standing among other
15+ min read
Simple Task Manager CLI Using NodeJS
A Task Manager is a very useful tool to keep track of your tasks, whether it's for personal use or a work-related project. In this article, we will learn how to build a Simple Task Manager CLI (Command Line Interface) application using Node.js. What We Are Going to Create?We will build a CLI task ma
5 min read