A fully functional Todo List application built using React, Context API, and Tailwind CSS. The app supports adding, editing, completing, and deleting todos, with persistent storage using localStorage.
- Add new todos
- Edit existing todos
- Mark todos as completed/uncompleted
- Delete todos
- Auto-save todos to
localStorage - Fully styled with Tailwind CSS
- Global state management using Context API
- βοΈ React (Hooks, Functional Components)
- π― Context API for state management
- πΎ LocalStorage for data persistence
- π¨ Tailwind CSS for styling
- π JavaScript (ES6+)
src/
β
βββ components/
β βββ TodoForm.jsx // Input field + Add button
β βββ TodoItem.jsx // Each todo item with edit, delete, complete
β
βββ contexts/
β βββ TodoContext.js // React Context logic for global state
β
βββ App.jsx // Main app with all logic and provider
βββ index.css / main.jsx // Tailwind CSS & entry point
We use createContext() to create TodoContext:
export const TodoContext = createContext(defaultValue)Then we wrap the app with the provider in App.jsx:
<TodoProvider value={{todos, addTodo, updateTodo, deleteTodo, toggleComplete}}>All components now have access to these via a custom hook:
export const useTodo = () => useContext(TodoContext);We keep the main todo list in state:
const [todos, setTodos] = useState([]);setTodos(prev => [{ id: Date.now(), ...todo }, ...prev]);setTodos(prev => prev.map(t => t.id === id ? updatedTodo : t));setTodos(prev => prev.filter(t => t.id !== id));setTodos(prev => prev.map(t => t.id === id ? { ...t, completed: !t.completed } : t));To persist todos across reloads:
useEffect(() => {
const todos = JSON.parse(localStorage.getItem("todos"));
if (todos?.length) setTodos(todos);
}, []);useEffect(() => {
localStorage.setItem("todos", JSON.stringify(todos));
}, [todos]);- Uses
useStateto manage input text - Calls
addTodo()from context on submit - Clears input after adding
addTodo({ todo, completed: false });Each todo can be:
- Toggled (via checkbox)
- Edited (via input + save button)
- Deleted (via β button)
The component uses:
updateTodo(todo.id, { ...todo, todo: todoMsg });
toggleComplete(todo.id);
deleteTodo(todo.id);It manages local state for editing via:
const [isTodoEditable, setIsTodoEditable] = useState(false);Date.now()used for unique IDs.- Spread operator (
{ ...todo, todo: updatedMessage }) ensures immutability. - Controlled inputs maintain form data in state.
- Tailwind CSS provides fast styling without writing custom CSS.
git clone <repo-url>
cd <project-directory>
npm install
npm run devOpen http://localhost:5173 in your browser.
- Using Context API for clean state management across components
- Avoiding prop drilling with
useContext() - Leveraging custom hooks for cleaner access
- Persisting state with localStorage
- Practicing React patterns with functional components & hooks
- Add filters (All, Active, Completed)
- Use
uuidinstead ofDate.now()for better ID generation - Add animations with
Framer Motion - Add authentication and save todos per user using Firebase or Supabase
