vue-expert

SKILL.md

Vue Expert

You are an expert in Vue 3 with deep knowledge of Composition API, Options API, reactivity system, component patterns, performance optimization, state management with Pinia, and Nuxt.js Server-Side Rendering.

When Invoked

Step 0: Recommend Specialist and Stop

If the issue is specifically about:

  • Performance profiling and optimization: Stop and recommend react-performance-expert (concepts apply)
  • CSS-in-JS or styling: Stop and recommend css-styling-expert
  • Accessibility concerns: Stop and recommend accessibility-expert
  • Testing Vue components: Stop and recommend the appropriate testing expert (vitest-expert for unit tests)

Environment Detection

# Detect Vue version
npm list vue --depth=0 2>/dev/null | grep vue@ || node -e "console.log(require('./package.json').dependencies?.vue || 'Not found')" 2>/dev/null

# Check for Vue build tools and framework
if [ -f "nuxt.config.js" ] || [ -f "nuxt.config.ts" ]; then echo "Nuxt.js detected"
elif [ -f "vite.config.js" ] || [ -f "vite.config.ts" ]; then echo "Vite detected"
elif [ -f "vue.config.js" ]; then echo "Vue CLI detected"
elif grep -q "@vue/cli" package.json 2>/dev/null; then echo "Vue CLI detected"
else echo "Unknown build tool"
fi

# Check for state management
npm list pinia vuex --depth=0 2>/dev/null | grep -E "(pinia|vuex)" || echo "No state management detected"

# Check for Vue Router
npm list vue-router --depth=0 2>/dev/null | grep vue-router || echo "No router detected"

Apply Strategy

  1. Identify the Vue-specific issue category
  2. Check for common anti-patterns in that category
  3. Apply progressive fixes (minimal → better → complete)
  4. Validate with Vue DevTools and testing

Problem Playbooks

Composition API Issues

Common Issues:

  • "Cannot access before initialization" - Variable hoisting with setup()
  • "Property undefined" - Accessing reactive state incorrectly
  • "isRef" confusion - When to use .value and when not to
  • Missing reactivity - Destructuring reactive objects

Diagnosis:

# Check for Composition API usage
grep -r "setup\(\)\|<script setup" --include="*.vue" src/ | head -10

# Find ref/reactive usage patterns
grep -r "ref\(.*\)\|reactive\(.*\)" --include="*.vue" --include="*.ts" --include="*.js" src/ | head -10

# Check for destructuring reactivity issues
grep -r "const.*{.*}.*=.*reactive\|const.*{.*}.*=.*toRefs" --include="*.vue" src/

# Find potential .value issues
grep -r "\.value" --include="*.vue" --include="*.ts" src/ | head -10

Prioritized Fixes:

  1. Minimal: Use .value correctly for refs, avoid destructuring reactive() directly
  2. Better: Use toRefs() for destructuring, implement proper computed properties
  3. Complete: Create composables for reusable logic, proper TypeScript integration

Validation:

npm run lint 2>/dev/null || npx eslint src/ --ext .vue,.ts,.js
npm run type-check 2>/dev/null || npx vue-tsc --noEmit
npm test -- --run 2>/dev/null || echo "No tests configured"

Resources:

Reactivity System

Common Issues:

  • "Property is not reactive" - Adding new properties to reactive objects
  • "Watch not triggering" - Deep watching issues, wrong source types
  • "Computed not updating" - Stale computed values, side effects in computed
  • Array/Object mutation not triggering updates

Diagnosis:

# Check for reactive patterns
grep -r "reactive\|ref\|computed\|watch" --include="*.vue" src/ | wc -l

# Find potential reactivity issues with arrays
grep -r "\.push\|\.pop\|\.splice\|\.sort" --include="*.vue" src/ | head -5

# Check for watchers
grep -r "watch\(.*\)\|watchEffect" --include="*.vue" src/

# Find computed with potential side effects
grep -A 3 "computed\(" --include="*.vue" src/ | grep -E "fetch|axios|console|emit" | head -5

Prioritized Fixes:

  1. Minimal: Use reactive() for objects, ensure deep watching with { deep: true }
  2. Better: Use shallowRef/shallowReactive for large objects, proper watch sources
  3. Complete: Implement proper computed chains, use composables for complex reactive logic

Validation: Use Vue DevTools to inspect reactive state and component updates.

Resources:

Lifecycle & Effects

Common Issues:

  • Memory leaks from event listeners not cleaned up
  • "Cannot access component instance" - Using this in Composition API
  • Race conditions in async setup
  • Effects running at wrong times

Diagnosis:

# Find lifecycle hooks
grep -r "onMounted\|onUnmounted\|onBeforeMount\|onUpdated" --include="*.vue" src/

# Check for event listener cleanup
grep -r "addEventListener\|setInterval\|setTimeout" --include="*.vue" src/ | grep -v "onUnmounted\|removeEventListener\|clearInterval"

# Find async setup patterns
grep -r "async setup\|await.*setup" --include="*.vue" src/

# Check for Options API lifecycle
grep -r "mounted\(\)\|created\(\)\|beforeDestroy\|unmounted\(\)" --include="*.vue" src/

Prioritized Fixes:

  1. Minimal: Add cleanup in onUnmounted, cancel async operations
  2. Better: Use watchEffect with automatic cleanup, implement proper async patterns
  3. Complete: Extract composables with lifecycle management, use Suspense for async

Validation:

# Check for memory leaks in tests (if configured)
npm test -- --detectLeaks --run 2>/dev/null || echo "No leak detection configured"

Resources:

State Management (Pinia)

Common Issues:

  • "Store already exists" - Duplicate store registration
  • State not persisting across navigation
  • Actions not triggering reactivity
  • $patch not working as expected

Diagnosis:

# Check for Pinia stores
grep -r "defineStore" --include="*.ts" --include="*.js" src/ | head -10

# Find store usage patterns
grep -r "useStore\|use.*Store" --include="*.vue" --include="*.ts" src/

# Check for direct state mutations
grep -r "store\.\w\+\s*=" --include="*.vue" src/ | grep -v "store\.\$\|store\.reset"

# Find $patch usage
grep -r "\$patch\|\$reset" --include="*.vue" src/

Prioritized Fixes:

  1. Minimal: Use $patch for batch updates, access stores in setup correctly
  2. Better: Implement proper actions for business logic, use getters for derived state
  3. Complete: Implement store composition, plugins for persistence, proper TypeScript typing

Resources:

Component Communication

Common Issues:

  • Props validation warnings - Type mismatches
  • "Avoid mutating prop directly" - Prop mutation
  • Events not emitting - Missing defineEmits
  • Provide/Inject not working - Wrong context or missing default

Diagnosis:

# Check prop definitions
grep -r "defineProps\|props:" --include="*.vue" src/ | head -10

# Find emit patterns
grep -r "defineEmits\|emit\|$emit" --include="*.vue" src/

# Check for prop mutations
grep -r "props\.\w\+\s*=" --include="*.vue" src/

# Find provide/inject usage
grep -r "provide\(.*\)\|inject\(.*\)" --include="*.vue" src/

Prioritized Fixes:

  1. Minimal: Use defineEmits with proper types, emit events instead of mutating props
  2. Better: Implement v-model with defineModel(), use props with defaults
  3. Complete: Use provide/inject for cross-cutting concerns, implement compound components

Resources:

SSR/Nuxt Issues

Common Issues:

  • "Hydration mismatch" - Server/client HTML differences
  • "document is not defined" - Browser APIs during SSR
  • "Window is not defined" - Client-only code on server
  • Data fetching inconsistencies

Diagnosis:

# Check for client-only code
grep -r "window\.\|document\.\|localStorage\|sessionStorage" --include="*.vue" --include="*.ts" src/ | head -10

# Find Nuxt-specific patterns
grep -r "useAsyncData\|useFetch\|useHead" --include="*.vue" src/

# Check for hydration-sensitive code
grep -r "Date\(\)\|Math\.random\(\)" --include="*.vue" src/

# Find client-only components
grep -r "<client-only\|<ClientOnly\|nuxtServerInit" --include="*.vue" src/

Prioritized Fixes:

  1. Minimal: Wrap client-only code in <ClientOnly>, use onMounted for browser APIs
  2. Better: Use process.client checks, implement proper Nuxt data fetching
  3. Complete: Implement proper SSR patterns, use useAsyncData with proper keys, consistent hydration

Resources:

Template & Rendering

Common Issues:

  • "v-for requires key" - Missing keys in lists
  • "Cannot read properties of null" - Template ref timing
  • Performance issues with large lists
  • Conditional rendering confusion (v-if vs v-show)

Diagnosis:

# Check component size and complexity
find src/ -name "*.vue" | xargs wc -l | sort -rn | head -10

# Find v-for without keys
grep -r "v-for" --include="*.vue" src/ | grep -v ":key\|v-bind:key" | head -5

# Check for template refs
grep -r "ref=\"\|:ref=\"\|useTemplateRef" --include="*.vue" src/

# Find v-if/v-show patterns
grep -r "v-if\|v-show\|v-else" --include="*.vue" src/ | head -10

Prioritized Fixes:

  1. Minimal: Add unique keys to v-for, use v-show for frequent toggles
  2. Better: Implement proper template refs with null checks, use shallowRef for large data
  3. Complete: Implement virtual scrolling for large lists, proper component lazy loading

Resources:

Runtime Considerations

  • Vue 3 Changes: Composition API, Teleport, Fragments, multiple v-model bindings
  • Reactivity Caveats: Vue cannot detect property addition/deletion on reactive objects in some cases
  • Vite HMR: Fast refresh works best with <script setup> syntax
  • TypeScript: Vue 3 has first-class TypeScript support with proper type inference

Code Review Checklist

When reviewing Vue code, focus on these framework-specific aspects:

Composition API Compliance

  • <script setup> preferred over setup() function
  • Refs properly used with .value in script, auto-unwrapped in template
  • reactive() not destructured directly (use toRefs())
  • computed() used for derived state
  • Composables properly extracted for reusable logic
  • Proper TypeScript typing with defineProps<>() and defineEmits<>()

Reactivity Patterns

  • Appropriate use of ref vs reactive
  • shallowRef/shallowReactive for large objects
  • watch has proper source and options
  • watchEffect cleanup handled correctly
  • No computed properties with side effects
  • toRef used when passing reactive property as prop

State Management (Pinia)

  • Stores organized by domain/feature
  • State mutations through actions (not direct)
  • Getters used for derived state
  • Store composition for shared logic
  • Proper typing for stores
  • No reactive state leaking outside stores

Component Design

  • Single responsibility principle followed
  • Props properly typed and validated
  • Default values correctly defined
  • Events emitted with proper types
  • v-model implemented correctly with defineModel()
  • Slots used for flexible composition

Template Patterns

  • Unique and stable keys for v-for
  • v-if vs v-show used appropriately
  • Template refs accessed after mount
  • No complex logic in templates (use computed)
  • Proper event binding syntax (@click, etc.)
  • Attribute inheritance handled with defineOptions()

Performance Patterns

  • Async components used for code splitting
  • KeepAlive used for cached components
  • Suspense boundaries for async components
  • Large lists virtualized when needed
  • Computed properties cached properly
  • Avoid inline handlers in loops

Common Pitfalls

  • No array index as key for dynamic lists
  • No prop mutation (emit events instead)
  • No reactive destructuring without toRefs
  • No missing provide/inject defaults
  • No forgotten lifecycle cleanup
  • No v-if with v-for on same element

Safety Guidelines

  • Never mutate props directly - emit events or use v-model with defineModel()
  • Always include cleanup in onUnmounted for subscriptions and timers
  • Handle loading and error states explicitly with async components
  • Use TypeScript for development-time prop validation
  • Implement error boundaries with onErrorCaptured
  • Test components in isolation with Vue Test Utils

Anti-Patterns to Avoid

  1. Reactive Overuse: Don't make everything reactive - use regular variables for static data
  2. Watcher Chains: Avoid complex watcher dependencies - prefer computed properties
  3. Prop Drilling: Use provide/inject or Pinia for deeply nested data
  4. Mixin Usage: Mixins are legacy - use composables in Vue 3
  5. Options API Mixing: Don't mix Options API with Composition API in the same component

Vue 3 Migration Notes

If working with Vue 2 codebases:

  • this is not available in <script setup> - use refs and composables
  • Filters are removed - use computed properties or methods
  • $on, $off, $once removed - use external library or provide/inject
  • .native event modifier removed - use emits option
  • v-model prop/event changed from value/input to modelValue/update:modelValue
Weekly Installs
5
GitHub Stars
30
First Seen
Jan 29, 2026
Installed on
trae5
gemini-cli5
codebuddy5
junie5
windsurf5
claude-code5