Developing a Task Manager application using Vue 3 focused on:
- Component composition
- Props and events
- State management
- Composables
- Clean structure and separation of concerns
What the Task Manager App Does
The Task Manager allows users to:
- Add tasks
- Mark tasks as completed
- Delete tasks
- Toggle all tasks (Check All / Uncheck All)
- Delete all tasks
- Switch between dark and light mode
- Edit tasks inline
PLANNING PHASE
Before writing code, I thought about:
- Where should the task state live?
- How will components communicate?
- Should I use local state or a shared composable?
Breaking Down the Problem
I split the app into smaller parts:
- TaskManager (parent)
- CardViewComponent
- CheckAllBtns
- EmptyState
- useTasks (composable)
This made the app easier to understand and maintain, though I didn't
plan all the UX requirements at first.
KEY DESIGN DECISIONS
-
Composable for State Management
- Problem: Multiple components needed access to task data
- Decision: Created useTasks composable
- Reason: Centralizes task data and most operations
-
Separate Components
- Problem: App growing too large
- Decision: Split into focused components
- Reason: Easier to read, maintain, and debug
-
Slots in EmptyState
- Problem: Need flexible empty state content
- Decision: Use slots instead of props
- Reason: Slots pass content (HTML), props pass data
-
CSS Classes for Theming (Not Dynamic Components)
- Initial mistake: Created both CardView and ListView thinking I needed separate components for light and dark modes
- Problem: Both components had identical code only CSS colors differed
- Realization: Dark/light mode is a styling concern, not a component structure concern
- Decision: Deleted ListView, used CSS class binding instead:
:class="isDark ? 'dark' : 'light'" - Reason: One component with dynamic CSS classes is cleaner than duplicating components for themes
- Lesson: Dynamic components are for structurally different layouts (grid vs list), not for styling variations (light vs dark)
CHALLENGES & SOLUTIONS
Challenge 1: Misunderstanding Dynamic Components and Duplicate State
- What went wrong: Initially thought I needed separate components (CardView and ListView) for light and dark themes. While building ListView, I created a local tasks array in both files
- Problems discovered:
- Both components had identical structure only CSS differed (wrong use of dynamic components)
- ListView's local tasks array didn't sync with CardView's composable based tasks (duplicate state)
- Why sync failed: CardView and ListView were using two separate task arrays in different locations, not one shared source
- Fix:
- Realized theming is a CSS concern, not a component concern
- Deleted ListView entirely
- Implemented theming with CSS class binding:
:class="isDark ? 'dark' : 'light'"
- Lessons learned:
- Dynamic components = Different structures/layouts (e.g., grid vs list)
- CSS classes = Different styles/themes (e.g., light vs dark)
- Multiple views must share the SAME data source from composable, not create separate copies
- Don't duplicate components when CSS can solve the problem
Challenge 2: deleteAllTasks location
- What went wrong: Placed in child component initially
- Problem: Child shouldn't manage global state
- Fix: Moved to parent component, triggered via events
- Current state: Still in parent (TaskManager.vue)
- Lesson: Child emits events; parent/composable manages state
Challenge 3: Missing UX requirements
- What went wrong: Didn't plan for:
- Text overflow (long task names)
- Empty state display
- Visual feedback for completed tasks
- Fix:
- Added truncateText() function (max 30 characters)
- Created EmptyState component
- Added linethrough styling for completed tasks
- Lesson: Plan UX requirements well before implementation, not just functionality
Challenge 4: Slots vs Props
- What went wrong: Tried passing empty state content via props
- Problem: Props only handle simple data (strings, numbers), not HTML or components
- Fix: Used slots for flexible content can now pass any HTML, icons, or components
- Lesson: Props = data, Slots = content
KEY TAKEAWAYS
Component vs CSS: Don't create separate components for styling differences use CSS classes for themes. Dynamic components should only be used when components are structurally different.
Single Source of Truth: Multiple views must share the SAME data source (composable), not create duplicate state. Changes to shared data should reflect everywhere immediately.
Architectural Consistency: All operations on shared state should live in one place (composable) for consistency and maintainability.
UX Planning: Important UX requirements (empty states, text handling, visual feedback) should be considered during planning, not added during execution.
-
Understanding Tools:
- Dynamic components = Different structure/functionality (CardView grid layout vs ListView vertical layout)
- CSS classes = Different appearance/theme (light mode vs dark mode)
- Using the wrong tool leads to code duplication and complexity
Planning and breaking the app into components made implementation easier. Recognizing when I was overcomplicating things (separate components for themes) and understanding the right tool for each job (CSS vs dynamic components) were important lessons in building maintainable Vue applications.
Top comments (0)