A simple Task Manager application built with React to practice modern frontend concepts including component architecture, hooks, routing, browser APIs, and UI composition.
The project demonstrates how to build a small but complete React application using functional components, state management with hooks, localStorage persistence, and client-side routing.
This application allows users to:
- Create tasks with title and description
- Mark tasks as completed
- Delete tasks
- Persist tasks locally in the browser
- Navigate to a Task Details page using URL query parameters
The project was designed as a learning-focused architecture to practice:
- React component design
- Hooks (
useState,useEffect) - React Router
- Browser APIs
- Reusable UI components
- Basic UI composition with TailwindCSS
Main features include:
- Task creation form
- Task list with completion toggle
- Task deletion
- Task details page
- Local persistence using
localStorage
Frontend
- React
- Vite
- React Router DOM
- TailwindCSS
- Lucide Icons
Browser APIs
- localStorage
- Fetch API (example included)
src
│
├── components
│ ├── AddTask.jsx
│ ├── Button.jsx
│ ├── Input.jsx
│ ├── Tasks.jsx
│ └── Title.jsx
│
├── pages
│ └── TaskPage.jsx
│
├── App.jsx
├── main.jsx
│
public
assets
The project follows a component-driven architecture commonly used in React applications.
Main layers
| Layer | Responsibility |
|---|---|
| Pages | Route-level components |
| Components | Reusable UI components |
| App | Global state management |
| Router | Client-side navigation |
The global task state is managed in App.jsx using React's useState hook.
const [tasks, setTasks] = useState(
JSON.parse(localStorage.getItem("tasks")) || []
);Tasks are stored as objects:
{
id: number,
title: string,
description: string,
isCompleted: boolean
}
useEffect is used to persist the state to localStorage whenever tasks change.
useEffect(() => {
localStorage.setItem("tasks", JSON.stringify(tasks))
}, [tasks])This ensures tasks persist across browser reloads.
The UI is composed using small reusable components.
Example:
App
├── Title
├── AddTask
└── Tasks
├── Button
├── Button
└── Task Item
This pattern improves:
- readability
- reuse
- maintainability
Client-side navigation is implemented using React Router.
createBrowserRouter([
{ path: "/", element: <App /> },
{ path: "/task", element: <TaskPage /> }
])The Task Details page receives data through URL query parameters.
Example URL:
/task?title=Buy%20Milk&description=Go%20to%20the%20market
Query parameters are accessed using:
useSearchParams()
Reusable heading component for page titles.
A generic styled input component using prop spreading.
<input {...props} />
This allows flexible usage without redefining props.
Reusable button wrapper component.
Benefits:
- consistent styling
- reusable behavior
- clean JSX structure
Handles:
- controlled inputs
- validation
- task submission
Uses useState for local form state.
Responsible for:
- rendering the task list
- task interactions
- navigation to the task details page
Tasks are persisted using the browser localStorage API.
Advantages:
- no backend required
- fast
- simple for small applications
The project also contains a commented example of fetching tasks from an external API using fetch and useEffect.
This demonstrates a common React pattern:
useEffect
→ fetch API
→ parse JSON
→ update state
Example source used:
https://jsonplaceholder.typicode.com/todos
Clone the repository:
git clone https://github.com/your-username/task-manager.git
Enter the project directory:
cd task-manager
Install dependencies:
npm install
Start the development server:
npm run dev
Open in browser:
http://localhost:5173
Form inputs are implemented using controlled components:
value={title}
onChange={(e) => setTitle(e.target.value)}
Benefits:
- predictable state
- validation control
- easier debugging
Task updates use immutable patterns:
setTasks(tasks.map(task => ...))
This follows React best practices for state updates.
Callbacks are passed from parent to children:
onTaskClick
onDeleteTaskClick
onAddTaskSubmit
This keeps state centralized in App.jsx.
Task details are passed via query parameters, demonstrating how applications can store state in URLs.
Possible next steps for the project:
- Add TypeScript
- Add Context API or Zustand for global state
- Add Drag & Drop task ordering
- Add task editing
- Add dark mode
- Persist tasks using a backend API
- Add unit tests with Jest + React Testing Library