$npx -y skills add alexanderop/claude-skill-vue-development --skill vue-developmentUse when planning or implementing Vue 3 projects - helps architect component structure, plan feature implementation, and enforce TypeScript-first patterns with Composition API, defineModel for bindings, Testing Library for user-behavior tests, and MSW for API mocking. Especially
| 1 | # Vue Development |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | Modern Vue 3 development with TypeScript, Composition API, and user-behavior testing. **Core principle:** Use TypeScript generics (not runtime validation), modern APIs (defineModel not manual props), and test user behavior (not implementation details). |
| 6 | |
| 7 | ## Red Flags - STOP and Fix |
| 8 | |
| 9 | If you catch yourself thinking or doing ANY of these, STOP: |
| 10 | |
| 11 | - "For speed" / "quick demo" / "emergency" → Using shortcuts |
| 12 | - "We can clean it up later" → Accepting poor patterns |
| 13 | - "TypeScript is too verbose" → Skipping types |
| 14 | - "This is production-ready" → Without type safety |
| 15 | - "Following existing code style" → When existing code uses legacy patterns |
| 16 | - "Task explicitly stated..." → Following bad requirements literally |
| 17 | - Using `const props = defineProps()` without using props in script |
| 18 | - Manual `modelValue` prop + `update:modelValue` emit → Use defineModel() |
| 19 | - "Component that takes value and emits changes" → Use defineModel(), NOT manual props/emit |
| 20 | - Using runtime prop validation when TypeScript is available |
| 21 | - Array syntax for emits: `defineEmits(['event'])` → Missing type safety |
| 22 | - `setTimeout()` in tests → Use proper async utilities |
| 23 | - Testing `wrapper.vm.*` internal state → Test user-visible behavior |
| 24 | - Using `index.vue` in routes → Use route groups `(name).vue` |
| 25 | - Generic route params `[id]` → Use explicit `[userId]`, `[postSlug]` |
| 26 | - Composables calling `showToast()`, `alert()`, or modals → Expose error state, component handles UI |
| 27 | - External composable used in only ONE component → Start inline, extract when reused |
| 28 | |
| 29 | **All of these mean: Use the modern pattern. No exceptions.** |
| 30 | |
| 31 | ## Quick Rules |
| 32 | |
| 33 | **Components:** `defineProps<{ }>()` (no const unless used in script), `defineEmits<{ event: [args] }>()`, `defineModel<type>()` for v-model. See @references/component-patterns.md |
| 34 | |
| 35 | **Testing:** `@testing-library/vue` + MSW. Use `findBy*` or `waitFor()` for async. NEVER `setTimeout()` or test internal state. See @references/testing-patterns.md |
| 36 | |
| 37 | **Routing:** Explicit params `[userId]` not `[id]`. Avoid `index.vue`, use `(name).vue`. Use `.` for nesting: `users.edit.vue` → `/users/edit`. See @references/routing-patterns.md |
| 38 | |
| 39 | **Composables:** START INLINE for component-specific logic, extract to external file when reused. External composables: prefix `use`, NO UI logic (expose error state instead). See @references/composable-patterns.md |
| 40 | |
| 41 | ## Key Pattern: defineModel() |
| 42 | |
| 43 | The most important pattern to remember - use for ALL two-way binding: |
| 44 | |
| 45 | ```vue |
| 46 | <script setup lang="ts"> |
| 47 | // ✅ For simple v-model |
| 48 | const value = defineModel<string>({ required: true }) |
| 49 | |
| 50 | // ✅ For multiple v-models |
| 51 | const firstName = defineModel<string>('firstName') |
| 52 | const lastName = defineModel<string>('lastName') |
| 53 | </script> |
| 54 | |
| 55 | <template> |
| 56 | <input v-model="value" /> |
| 57 | <!-- Parent uses: <Component v-model="data" /> --> |
| 58 | </template> |
| 59 | ``` |
| 60 | |
| 61 | **Why:** Reduces 5 lines of boilerplate to 1. No manual `modelValue` prop + `update:modelValue` emit. |
| 62 | |
| 63 | ## Component Implementation Workflow |
| 64 | |
| 65 | When implementing complex Vue components, use TodoWrite to track progress: |
| 66 | |
| 67 | ``` |
| 68 | TodoWrite checklist for component implementation: |
| 69 | - [ ] Define TypeScript interfaces for props/emits/models |
| 70 | - [ ] Implement props with defineProps<{ }>() (no const unless used in script) |
| 71 | - [ ] Implement emits with defineEmits<{ event: [args] }>() |
| 72 | - [ ] Add v-model with defineModel<type>() if needed |
| 73 | - [ ] Write user-behavior tests with Testing Library |
| 74 | - [ ] Test async behavior with findBy* queries or waitFor() |
| 75 | - [ ] Verify: No red flags, no setTimeout in tests, all types present |
| 76 | ``` |
| 77 | |
| 78 | **When to create TodoWrite todos:** |
| 79 | - Implementing new components with state, v-model, and testing |
| 80 | - Refactoring components to modern patterns |
| 81 | - Adding routing with typed params |
| 82 | - Creating composables with async logic |
| 83 | |
| 84 | ## Rationalizations Table |
| 85 | |
| 86 | | Excuse | Reality | |
| 87 | |--------|---------| |
| 88 | | "For speed/emergency/no time" | Correct patterns take SAME time. TypeScript IS fast. | |
| 89 | | "TypeScript is too verbose" | `defineProps<{ count: number }>()` is LESS code. | |
| 90 | | "We can clean it up later" | Write it right the first time. | |
| 91 | | "This is production-ready" | Without type safety, it's not production-ready. | |
| 92 | | "Simple array syntax is fine" | Missing types = runtime errors TypeScript would catch. | |
| 93 | | "Manual modelValue was correct" | That was Vue 2. Use defineModel() in Vue 3.4+. | |
| 94 | | "Tests are flaky, add timeout" | Timeouts mask bugs. Use proper async handling. | |
| 95 | | "Following existing code style" | Legacy code exists. Use modern patterns to improve. | |
| 96 | | "Task explicitly stated X" | Understand INTENT. Bad requirements need good implementation. | |
| 97 | | "Composables can |