All skills
hyf0 avatar

/vue-debug-guides

@a5fc891 official
by hyf0hyf0/vue-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/hyf0/vue-skills/vue-debug-guides

This session only. Nothing lands on disk.

referenceundeclared-emits-double-firing.md

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

Undeclared Emits Cause Double Event Firing

Impact: HIGH - When a component re-emits a native DOM event (like click) without declaring it in emits, the event can fire twice. This happens because undeclared event listeners become part of $attrs and fall through to the root element, where they listen for native events while your emit() call also fires.

This is a common bug when wrapping native elements or migrating from Vue 2 to Vue 3.

Task Checklist

  • Declare all emitted events in defineEmits() or emits option
  • Pay special attention when re-emitting native events (click, input, change, etc.)
  • Check for double firing if component wraps native elements
  • Understand that undeclared listeners fall through to $attrs

The Problem

Incorrect - Undeclared emit causes double firing:

<!-- MyButton.vue -->
<script setup>
// NO defineEmits declaration!
</script>

<template>
  <!-- Native click listener from parent falls through to button -->
  <!-- PLUS we re-emit click -->
  <button @click="$emit('click', $event)">
    <slot></slot>
  </button>
</template>
<!-- Parent.vue -->
<template>
  <!-- This handler fires TWICE on each click! -->
  <MyButton @click="handleClick">Click me</MyButton>
</template>

<script setup>
function handleClick() {
  console.log('clicked') // Logs twice!
}
</script>

What happens:

  1. User clicks the button
  2. Native click event fires on the button element
  3. @click falls through to button (because 'click' isn't in emits), triggering handleClick
  4. The button's @click="$emit('click', $event)" also fires, emitting a component event
  5. Parent's @click="handleClick" receives the emitted event, triggering handleClick again

The Solution

Correct - Declare the emit:

<!-- MyButton.vue -->
<script setup>
// Declare 'click' as a component event
const emit = defineEmits(['click'])
</script>

<template>
  <!-- Now @click="handleClick" in parent only listens to emit() -->
  <button @click="emit('click', $event)">
    <slot></slot>
  </button>
</template>
<!-- Parent.vue -->
<template>
  <!-- Now fires only once -->
  <MyButton @click="handleClick">Click me</MyButton>
</template>

When you declare click in emits:

  • Vue knows @click on the component is listening for a component event
  • The listener does NOT fall through to the root element
  • Only your explicit emit('click') triggers the parent's handler

Options API Version

Correct - Using emits option:

<script>
export default {
  emits: ['click', 'input', 'change'],

  methods: {
    handleClick(event) {
      this.$emit('click', event)
    }
  }
}
</script>

Common Scenarios

Wrapping Form Inputs

<!-- CustomInput.vue -->
<script setup>
// Declare all events you re-emit
const emit = defineEmits(['input', 'change', 'focus', 'blur'])
</script>

<template>
  <input
    @input="emit('input', $event)"
    @change="emit('change', $event)"
    @focus="emit('focus', $event)"
    @blur="emit('blur', $event)"
  />
</template>

Wrapper Components

<!-- IconButton.vue -->
<script setup>
const emit = defineEmits(['click'])
</script>

<template>
  <button @click="emit('click', $event)">
    <Icon :name="icon" />
    <slot></slot>
  </button>
</template>

Alternative: Don't Re-emit, Let It Fall Through

If your component has a single root element and you want native event behavior:

<!-- MyButton.vue -->
<script setup>
// Don't declare 'click' - let it fall through naturally
const emit = defineEmits(['custom-action'])
</script>

<template>
  <!-- Native @click from parent falls through to this button -->
  <button>
    <slot></slot>
  </button>
</template>
<!-- Parent.vue -->
<template>
  <!-- This native click falls through to the button -->
  <MyButton @click="handleClick">Click me</MyButton>
</template>

This works because:

  • You don't re-emit 'click' explicitly
  • The native click listener falls through to the single root button
  • Native click fires once when button is clicked

Debugging Double Firing

<script setup>
function handleClick(event) {
  console.log('Event type:', event?.type)
  console.log('Is native:', event instanceof Event)
  console.trace('Click handler called')
}
</script>

If you see two stack traces with different origins, you have the double-firing issue.

Reference

Source: SKILL.md on GitHub

No alerts17d5 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 files scanned · No issues

  • 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 hyf0/vue-skills/vue-debug-guides

Guides for diagnosing and fixing Vue 3 runtime errors, reactivity bugs, async failures, and hydration issues across components, templates, watchers, forms, and lifecycle hooks. Covers specific failure modes like stale watchers, ref unwrapping edge cases, v-model sync problems, and SSR mismatches with concrete reference links for each issue.

Generated from the current SKILL.md.

Does this skill cover Vue 2?
No. This skill is specific to Vue 3 debugging and error handling.
What should I use for Vue development best practices instead of debugging?
The skill references a separate `vue-best-practices` skill for development best practices and common gotchas.
Does this cover SSR and hydration issues?
Yes. The skill includes guides for diagnosing and fixing SSR rendering differences and hydration bugs.
Can this help with async watcher and watchEffect problems?
Yes. The skill covers async operation stale data, dependency tracking after await, and proper flush timing for watchers.
Does this address v-model and form-related issues?
Yes. The skill includes guides for v-model initial values, textarea interpolation, IME composition, and custom checkbox form submission.

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