All skills
vuejs-ai avatar

/vue-debug-guides

@a5fc891 official
by Vue AIvuejs-ai/skills2.9k stars
167

Vue 3 debugging and error handling for runtime errors, warnings, async failures, and SSR/hydration issues. Use when diagnosing or fixing Vue issues.

Use this Skill: https://skilld.dev/gh/vuejs-ai/skills/vue-debug-guides

This session only. Nothing lands on disk.

referencets-event-handler-explicit-typing.md

≈1.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Always Explicitly Type Event Handlers

Impact: MEDIUM - Native DOM event handlers in Vue components have implicit any type for the event parameter. In TypeScript strict mode, this causes errors. You must explicitly type event parameters and use type assertions for event.target.

Task Checklist

  • Always type the event parameter explicitly (e.g., Event, MouseEvent)
  • Use type assertions when accessing element-specific properties
  • Consider using inline handlers for simple cases
  • Be aware of Vue's synthetic event system

The Problem

<script setup lang="ts">
// WRONG: event has implicit 'any' type
function handleChange(event) {  // Error in strict mode!
  console.log(event.target.value)  // Also error: target might be null
}

// WRONG: Missing type assertion for element access
function handleInput(event: Event) {
  console.log(event.target.value)  // Error: 'value' doesn't exist on EventTarget
}
</script>

<template>
  <input @change="handleChange" @input="handleInput" />
</template>

The Solution

<script setup lang="ts">
// CORRECT: Explicit Event type + type assertion
function handleChange(event: Event) {
  const target = event.target as HTMLInputElement
  console.log(target.value)
}

// CORRECT: Specific event type when needed
function handleClick(event: MouseEvent) {
  console.log(event.clientX, event.clientY)
}

function handleKeydown(event: KeyboardEvent) {
  if (event.key === 'Enter') {
    submitForm()
  }
}

function handleSubmit(event: SubmitEvent) {
  event.preventDefault()
  const formData = new FormData(event.target as HTMLFormElement)
}
</script>

<template>
  <input @change="handleChange" />
  <button @click="handleClick">Click</button>
  <input @keydown="handleKeydown" />
  <form @submit="handleSubmit">...</form>
</template>

Common Event Types

Event Type Common Properties
click, dblclick MouseEvent clientX, clientY, button
keydown, keyup, keypress KeyboardEvent key, code, ctrlKey, shiftKey
input, change Event target (needs assertion)
focus, blur FocusEvent relatedTarget
submit SubmitEvent submitter
drag, dragstart, drop DragEvent dataTransfer
wheel, scroll WheelEvent deltaX, deltaY
touch events TouchEvent touches, changedTouches

Element-Specific Type Assertions

<script setup lang="ts">
// HTMLInputElement for text, number, checkbox, radio inputs
function handleTextInput(event: Event) {
  const input = event.target as HTMLInputElement
  console.log(input.value, input.checked)
}

// HTMLSelectElement for select dropdowns
function handleSelect(event: Event) {
  const select = event.target as HTMLSelectElement
  console.log(select.value, select.selectedIndex)
}

// HTMLTextAreaElement for textareas
function handleTextarea(event: Event) {
  const textarea = event.target as HTMLTextAreaElement
  console.log(textarea.value, textarea.selectionStart)
}

// HTMLFormElement for forms
function handleFormSubmit(event: SubmitEvent) {
  event.preventDefault()
  const form = event.target as HTMLFormElement
  const formData = new FormData(form)
}
</script>

Inline Event Handler Pattern

For simple cases, inline handlers with type annotations work well:

<template>
  <!-- Inline with type assertion -->
  <input
    @input="(event: Event) => {
      const input = event.target as HTMLInputElement
      searchQuery = input.value
    }"
  />

  <!-- Or with $event cast -->
  <input @input="searchQuery = ($event.target as HTMLInputElement).value" />
</template>

Generic Handler Pattern

Create reusable typed handlers:

// utils/events.ts
export function getInputValue(event: Event): string {
  return (event.target as HTMLInputElement).value
}

export function getSelectValue(event: Event): string {
  return (event.target as HTMLSelectElement).value
}

export function getCheckboxChecked(event: Event): boolean {
  return (event.target as HTMLInputElement).checked
}
<script setup lang="ts">
import { getInputValue, getCheckboxChecked } from '@/utils/events'

const name = ref('')
const agreed = ref(false)
</script>

<template>
  <input @input="e => name = getInputValue(e)" />
  <input type="checkbox" @change="e => agreed = getCheckboxChecked(e)" />
</template>

Vue Component Events

For Vue component events (not DOM events), use defineEmits for type safety:

<script setup lang="ts">
const emit = defineEmits<{
  'custom-event': [data: { id: number; name: string }]
}>()

// Handler for child component event
function handleChildEvent(data: { id: number; name: string }) {
  console.log(data.id, data.name)
}
</script>

<template>
  <!-- Custom component event - properly typed -->
  <ChildComponent @custom-event="handleChildEvent" />
</template>

Avoiding currentTarget vs target Confusion

function handleClick(event: MouseEvent) {
  // target: The element that triggered the event (could be a child)
  const target = event.target as HTMLElement

  // currentTarget: The element the listener is attached to
  const currentTarget = event.currentTarget as HTMLButtonElement

  // Be explicit about which you need
  if (target.tagName === 'SPAN') {
    // Clicked on span inside button
  }
}

Reference

Source: SKILL.md on GitHub

1 warning17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill is a comprehensive collection of educational debugging guides and best practices for Vue 3 developers. It contains no executable code or malicious instructions and focuses solely on diagnosing common runtime errors and performance issues.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    140/140 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at a5fc891. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 8 months ago

README badge

README badge for vuejs-ai/skills/vue-debug-guides

Guides for diagnosing Vue 3 runtime errors, reactivity bugs, component lifecycle issues, and SSR hydration problems. Covers reactivity traps (refs, computed, watchers), component mechanics (props, emits, slots), templates (v-if, v-for, refs), forms (v-model), and composition API gotchas.

Generated from the current SKILL.md.

Does this cover Vue 2 or only Vue 3?
This skill is for Vue 3 only. It addresses Vue 3-specific debugging patterns like reactivity proxies, script setup, and defineEmits/defineProps.
Does this skill help with performance optimization?
No. This skill focuses on runtime errors, warnings, and behavioral bugs. For development best practices and optimization, use the separate `vue-best-practices` skill.
What kinds of issues does this cover?
Reactivity traps (refs, computed, watchers), component lifecycle bugs, template directives, form binding edge cases, SSR/hydration mismatches, slot scoping, and async-related failures.
Does this include debugging for Nuxt or other Vue frameworks?
No. This skill focuses on core Vue 3 runtime issues. Framework-specific bugs are outside its scope.

Generated from the current SKILL.md. These answers refresh after source changes.