Project: Todo With Pinia
Introduction
This project builds a Todo app with Pinia, filtered views (all / active / completed), and optional localStorage persistence. No backend—focus on reactive UI, store design, and component structure before connecting APIs in later projects.
Prerequisites
Goals
| Feature | Skill |
|---|---|
| Add / toggle / remove todos | Actions + immutable updates |
| Filters all / active / done | Getters or computed in store |
| Persist todos | watch + localStorage |
| Component split | List, item, filter bar |
Store Design
src/stores/todos.ts:
Components
TodoInput.vue — emit or call store directly:
TodoItem.vue — props + events or store:
TodoFilter.vue:
TodoApp.vue:
Optional: Vitest Tests
Test store actions with createPinia()—Testing Vue Components.
Test addTodo ignores empty strings and filteredItems respects filter.
Deliverables
- Add, toggle, delete todos
- Three filters work correctly
-
activeCountupdates - Todos survive page refresh (localStorage)
- Empty state message when no todos
- Optional: transition on add/remove
Extensions
- Edit todo inline (double-click → input)
- Route query
?filter=activesynced with store - Replace localStorage with FastAPI CRUD in a follow-up branch
FAQ
Store vs composable for todos?
Single feature can use composable; Pinia practice prepares multi-view apps.
crypto.randomUUID?
Modern browsers; fallback Date.now() + random for older targets.
Pinia persistence plugin?
pinia-plugin-persistedstate replaces manual save()—optional upgrade.
Why emit from TodoItem?
Keeps item presentational—parent or store handles logic; both patterns valid.