debug:vue

Vue.js Debugging Guide

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "debug:vue" with this command: npx skills add snakeo/claude-debug-and-refactor-skills-plugin/snakeo-claude-debug-and-refactor-skills-plugin-debug-vue

Vue.js Debugging Guide

A systematic approach to debugging Vue.js 3 applications, covering common error patterns, debugging tools, and resolution strategies.

Common Error Patterns

  1. Reactivity Not Working

Symptoms:

  • Data changes but UI does not update

  • Computed properties return stale values

  • Watch callbacks not firing

Common Causes:

// WRONG: Adding new properties to reactive object const state = reactive({ count: 0 }) state.newProp = 'value' // Not reactive in Vue 2, works in Vue 3 with Proxy

// WRONG: Destructuring reactive objects const { count } = reactive({ count: 0 }) // count is NOT reactive

// WRONG: Replacing entire reactive object let state = reactive({ count: 0 }) state = reactive({ count: 1 }) // Lost reactivity connection

// WRONG: Using ref without .value const count = ref(0) count = 5 // Wrong! Use count.value = 5

Solutions:

// Use toRefs for destructuring const state = reactive({ count: 0 }) const { count } = toRefs(state) // count.value is reactive

// Use ref for primitives const count = ref(0) count.value++ // Correct

// Use shallowRef for large objects that don't need deep reactivity const largeData = shallowRef({ /* big object */ })

// Force reactivity update import { triggerRef } from 'vue' triggerRef(myShallowRef)

  1. Component Not Updating

Symptoms:

  • Props change but component doesn't re-render

  • Parent state updates don't propagate to children

  • v-for lists don't update correctly

Debugging Steps:

// 1. Check if prop is reactive watch(() => props.myProp, (newVal) => { console.log('Prop changed:', newVal) }, { immediate: true })

// 2. Verify key attribute on v-for <template> <!-- WRONG: index as key for dynamic lists --> <div v-for="(item, index) in items" :key="index">

<!-- CORRECT: unique identifier --> <div v-for="item in items" :key="item.id"> </template>

// 3. Check for prop mutation (anti-pattern) // Props should be immutable - emit events instead emit('update:modelValue', newValue)

  1. Pinia Store Issues

Symptoms:

  • Store state not updating across components

  • Actions not triggering reactivity

  • Getters returning stale data

Common Problems:

// WRONG: Destructuring store state const store = useMyStore() const { count } = store // NOT reactive!

// CORRECT: Use storeToRefs const store = useMyStore() const { count } = storeToRefs(store) // Reactive

// WRONG: Mutating state directly outside actions store.count++ // Works but bypasses devtools tracking

// CORRECT: Use actions or $patch store.increment() // Action store.$patch({ count: store.count + 1 }) // $patch

Debugging Pinia:

// Enable Pinia devtools tracking import { createPinia } from 'pinia' const pinia = createPinia()

// Subscribe to state changes store.$subscribe((mutation, state) => { console.log('Mutation:', mutation.type, mutation.storeId) console.log('New state:', state) })

// Subscribe to actions store.$onAction(({ name, args, after, onError }) => { console.log(Action ${name} called with:, args) after((result) => console.log(${name} returned:, result)) onError((error) => console.error(${name} failed:, error)) })

  1. Computed Property Caching Issues

Symptoms:

  • Computed returns same value despite dependency changes

  • Infinite loops in computed properties

  • Performance issues with computed

Solutions:

// Check dependencies are reactive const computed1 = computed(() => { // This won't update if nonReactiveValue changes return someRef.value + nonReactiveValue })

// Avoid side effects in computed // WRONG: const bad = computed(() => { someRef.value = 'changed' // Side effect! return otherRef.value })

// Debug computed dependencies const myComputed = computed(() => { console.log('Computed recalculating...') return expensiveOperation(dep1.value, dep2.value) })

  1. Teleport/Suspense Issues

Teleport Problems:

<!-- Ensure target exists before Teleport mounts --> <template> <!-- WRONG: target might not exist --> <Teleport to="#modal-container"> <Modal /> </Teleport>

<!-- CORRECT: conditional render --> <Teleport v-if="isMounted" to="#modal-container"> <Modal /> </Teleport> </template>

<script setup> import { ref, onMounted } from 'vue' const isMounted = ref(false) onMounted(() => { isMounted.value = true }) </script>

Suspense Problems:

<!-- Handle errors in Suspense --> <template> <Suspense> <template #default> <AsyncComponent /> </template> <template #fallback> <LoadingSpinner /> </template> </Suspense> </template>

<script setup> import { onErrorCaptured, ref } from 'vue'

const error = ref(null) onErrorCaptured((err) => { error.value = err return false // Stop propagation }) </script>

  1. SSR Hydration Mismatches

Symptoms:

  • Console warning: "Hydration mismatch"

  • Content flickers on page load

  • Different content between server and client

Common Causes and Solutions:

// WRONG: Browser-only code in setup const width = window.innerWidth // Fails on server

// CORRECT: Use onMounted for browser APIs const width = ref(0) onMounted(() => { width.value = window.innerWidth })

// CORRECT: Use ClientOnly component (Nuxt) <template> <ClientOnly> <BrowserOnlyComponent /> </ClientOnly> </template>

// Check for SSR vs client const isClient = typeof window !== 'undefined'

// Use useId() for consistent IDs import { useId } from 'vue' const id = useId() // Same on server and client

Debugging Tools

Vue DevTools

Installation:

  • Chrome: Vue.js devtools

  • Firefox: Vue.js devtools

Key Features:

  • Components Tab: Inspect component hierarchy, props, data, computed

  • Pinia Tab: View store state, actions, mutations

  • Timeline Tab: Track events, mutations, and performance

  • Routes Tab: Debug Vue Router (if using)

DevTools Tips:

// Access component instance in console // Select component in DevTools, then in console: $vm // Current component instance $vm.someMethod() // Call methods $vm.someData // Access data

// Inspect from DOM element // Right-click element > Inspect > Console: $0.__vueParentComponent // Parent component

Console Debugging

// Strategic console.log placement export default { setup() { const state = reactive({ count: 0 })

// Log reactive state changes
watch(
  () => ({ ...state }),
  (newState, oldState) => {
    console.log('State changed:', { old: oldState, new: newState })
  },
  { deep: true }
)

return { state }

} }

// Use console.table for arrays/objects console.table(items.value)

// Use console.trace for call stack function problematicFunction() { console.trace('Called from:') }

// Group related logs console.group('Component Mount') console.log('Props:', props) console.log('State:', state) console.groupEnd()

Debugger Statement

// Pause at specific points function handleClick() { debugger // Execution pauses here // Inspect scope, call stack, evaluate expressions processData() }

// Conditional debugging watch(count, (val) => { if (val > 10) { debugger // Only pause when condition met } })

Vite Dev Server

Enable verbose logging

vite --debug

Check for HMR issues

vite --force # Clear cache

Common vite.config.js debugging options

export default defineConfig({ server: { hmr: { overlay: true // Show errors as overlay } }, build: { sourcemap: true // Enable source maps } })

vue-tsc Type Checking

Run type checking

npx vue-tsc --noEmit

Watch mode

npx vue-tsc --noEmit --watch

Check specific files

npx vue-tsc --noEmit src/components/MyComponent.vue

The Four Phases of Vue Debugging

Phase 1: Identify the Error Type

// 1. Check browser console for errors // 2. Categorize the error: // - Template error (compilation) // - Runtime error (JavaScript) // - Reactivity issue (no error, wrong behavior) // - Network error (API/async)

// Set up global error handler app.config.errorHandler = (err, instance, info) => { console.error('Vue Error:', err) console.log('Component:', instance?.$options?.name || 'Unknown') console.log('Error Info:', info) // Send to error tracking service }

// Set up warning handler (development) app.config.warnHandler = (msg, instance, trace) => { console.warn('Vue Warning:', msg) console.log('Trace:', trace) }

Phase 2: Isolate the Problem

// 1. Simplify the component // 2. Remove code until error disappears // 3. Add code back incrementally

// Use error boundaries to isolate const ErrorBoundary = defineComponent({ setup(_, { slots }) { const error = ref(null)

onErrorCaptured((err, instance, info) => {
  error.value = { err, info }
  return false  // Prevent propagation
})

return () => error.value
  ? h('div', { class: 'error' }, `Error: ${error.value.err.message}`)
  : slots.default?.()

} })

// Wrap suspicious components <template> <ErrorBoundary> <SuspiciousComponent /> </ErrorBoundary> </template>

Phase 3: Investigate Root Cause

// Check component lifecycle onBeforeMount(() => console.log('beforeMount')) onMounted(() => console.log('mounted')) onBeforeUpdate(() => console.log('beforeUpdate')) onUpdated(() => console.log('updated')) onBeforeUnmount(() => console.log('beforeUnmount')) onUnmounted(() => console.log('unmounted'))

// Track prop changes watch(() => props, (newProps) => { console.log('Props changed:', JSON.stringify(newProps, null, 2)) }, { deep: true, immediate: true })

// Monitor emitted events const emit = defineEmits(['update']) function emitUpdate(value) { console.log('Emitting update:', value) emit('update', value) }

Phase 4: Apply and Verify Fix

// 1. Apply the smallest possible fix // 2. Verify fix doesn't break other functionality // 3. Add test to prevent regression

// Example: Add defensive checks const displayValue = computed(() => { if (!props.data) { console.warn('MyComponent: data prop is undefined') return 'N/A' } return props.data.value ?? 'N/A' })

// Add type safety interface Props { data?: { value: string } } const props = withDefaults(defineProps<Props>(), { data: undefined })

Quick Reference Commands

Development

Start dev server with debugging

npm run dev -- --debug

Type check

npm run type-check

or: npx vue-tsc --noEmit

Lint and fix

npm run lint -- --fix

Run unit tests

npm run test:unit

Run tests in watch mode

npm run test:unit -- --watch

Build Debugging

Build with source maps

npm run build -- --sourcemap

Analyze bundle size

npx vite-bundle-analyzer

Preview production build locally

npm run preview

Common Fixes

// Force component re-render const key = ref(0) function forceRerender() { key.value++ } // <MyComponent :key="key" />

// Clear reactive state Object.keys(state).forEach(key => delete state[key]) Object.assign(state, initialState)

// Reset Pinia store store.$reset()

// Flush pending updates import { nextTick } from 'vue' await nextTick() // DOM is now updated

Vue 3 Production Error Codes

Vue 3 uses short error codes in production. Reference:

  • Production Error Code Reference

// Decode production errors by searching the code // Example: Error code "0" = "This is a Vue internal error..."

Error Handling Best Practices

// main.js - Global error handling setup import { createApp } from 'vue' import App from './App.vue'

const app = createApp(App)

// Global error handler app.config.errorHandler = (err, instance, info) => { // Log to console in development if (import.meta.env.DEV) { console.error('Vue Error:', err) console.log('Component:', instance) console.log('Info:', info) }

// Send to error tracking in production if (import.meta.env.PROD) { // Sentry, LogRocket, etc. errorTracker.captureException(err, { extra: { component: instance?.$options?.name, info } }) } }

// Global warning handler (dev only) app.config.warnHandler = (msg, instance, trace) => { console.warn('Vue Warning:', msg) // Optionally treat warnings as errors in CI if (import.meta.env.CI) { throw new Error(Vue Warning: ${msg}) } }

app.mount('#app')

Resources

  • Vue.js Official Docs

  • Vue DevTools

  • Pinia Documentation

  • Vue School Articles

  • Vue.js Production Error Reference

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

refactor:flutter

No summary provided by upstream source.

Repository SourceNeeds Review
General

refactor:nestjs

No summary provided by upstream source.

Repository SourceNeeds Review
General

debug:flutter

No summary provided by upstream source.

Repository SourceNeeds Review