Vue 3 Composition API patterns, Pinia state management, reactivity, component design, and performance. Auto-activates on .vue files or Vue-related tasks. Use for Vue architecture decisions, composables, Pinia stores, forms, and optimization.
Scanned 5/29/2026
Install via CLI
openskills install ngocsangyem/MeowKit---
name: mk:vue
description: "Vue 3 Composition API patterns, Pinia state management, reactivity, component design, and performance. Auto-activates on .vue files or Vue-related tasks. Use for Vue architecture decisions, composables, Pinia stores, forms, and optimization."
source: aura-frog
original_skills: [vue-expert]
adapted_for: claude-code
keywords: [vue, composition-api, pinia, reactivity, composables, framework-specific]
when_to_use: "Use when working with Vue 3 Composition API, Pinia, reactivity, composables. Auto-activates on .vue files. NOT for React (see mk:react-patterns) or Angular."
user-invocable: true
---
# Vue 3 Expert
Vue 3 Composition API patterns, Pinia, reactivity, component design, forms, and performance.
> Use `npx chub search vue` for relevant documentation packages within the Context Hub
## When to Use
**Auto-activate on:** `.vue` files, Vue 3 Composition API, Pinia stores, `<script setup>`, Vue Router, composables
**Explicit:** `/mk:vue [concern]`
**Do NOT invoke for:** TypeScript fundamentals (use mk:typescript), visual design (use mk:frontend-design), testing (use mk:testing)
## Workflow Integration
Operates in **Phase 3 (Build GREEN)**. Output supports the `developer` agent.
## Process
1. **Detect concern** — component? composable? store? form? performance?
2. **Load relevant reference** — vue-patterns, pinia, or forms
3. **Apply patterns** — implement using Vue 3 best practices from references
4. **Verify** — component renders, types pass, no console warnings
## Core Rules (always apply)
- **ALWAYS** use `<script setup lang="ts">` — never Options API
- **ALWAYS** use `defineProps` with TypeScript interfaces — never runtime validation
- **ALWAYS** use `storeToRefs()` when destructuring Pinia store state
- **NEVER** use `v-html` with user content (security-rules.md — XSS vector)
- **PREFER** composables (`use*`) over mixins
- **PREFER** `ref()` for primitives, `reactive()` only for complex objects
- **NAME** components PascalCase, files kebab-case (`agent-conduct.md` (B1))
## Anti-Patterns
| Don't | Do Instead |
| ---------------------------------- | -------------------------------- |
| Options API (`data()`, `methods:`) | Composition API `<script setup>` |
| `this.$store` / Vuex | Pinia with setup store syntax |
| Direct store state destructuring | `storeToRefs(useMyStore())` |
| `v-html` with dynamic content | `v-text` or sanitized rendering |
| `reactive()` for primitives | `ref()` for primitives |
| Watchers when computed works | `computed()` for derived state |
| Global event bus | `provide/inject` or Pinia |
| `defineComponent()` wrapper | `<script setup>` directly |
## Output Format
```
## Vue: {concern}
**Files:** {list of .vue files modified}
**Pattern:** {composition API | pinia store | composable | component}
### Implementation
{code changes with Vue 3 patterns applied}
### Verification
{component renders, no console warnings, types pass}
```
## References
| Reference | When to load | Content |
| --------------------------------------------------- | ------------------------- | ---------------------------------------------------------- |
| **[vue-patterns.md](./references/vue-patterns.md)** | Component/composable work | Composition API, reactivity, component design, performance |
## Failure Handling
| Failure | Recovery |
| ----------------------------------- | ------------------------------------------- |
| Vue 2 syntax detected (Options API) | Migrate to Composition API `<script setup>` |
| Pinia not installed | `npm install pinia` |
| Type errors in template | Fix with `defineProps<T>()` typing |
## Gotchas
- **Destructuring `reactive()` loses reactivity** — `const { count } = reactive({ count: 0 })` makes `count` a plain number; always use `toRefs()` or keep the reactive object intact, or switch to `ref()`.
- **`storeToRefs()` not called when destructuring Pinia store** — `const { user } = useAuthStore()` gives a non-reactive snapshot; `const { user } = storeToRefs(useAuthStore())` is required for reactive bindings.
- **`<script setup>` with `defineExpose()` is required for parent `ref` access** — calling `childRef.value.method()` from a parent always returns `undefined` unless the child explicitly `defineExpose({ method })`; omitting this is silent until runtime.
- **`:deep()` selector broken when component uses scoped styles + slot content** — slot content comes from the parent scope, so `:deep(.child-class)` in a scoped style block has no effect on slotted content; use `:slotted(.child-class)` instead.
- **`watchEffect` cleanup race on fast re-renders** — without calling the `onCleanup` callback to cancel async operations, a fast prop change triggers a second effect before the first resolves, causing stale state writes; always register cleanup via `watchEffect((onCleanup) => { onCleanup(() => controller.abort()) })`.
- **Pinia store state not hydrated in SSR (Nuxt/Vite SSR)** — calling `useMyStore()` outside a component setup context (e.g., in a top-level module) creates a store instance disconnected from the SSR app; always call stores inside `setup()` or pass the `pinia` instance explicitly via `useMyStore(pinia)`.
No comments yet. Be the first to comment!