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.

referencetransition-js-hooks-done-callback.md

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

JavaScript Transition Hooks Require done() Callback with css="false"

Impact: HIGH - When using JavaScript-only transitions (with :css="false"), the @enter and @leave hooks must call the done() callback to signal when the animation completes. Without calling done(), Vue considers the transition finished immediately, causing elements to appear/disappear without animation.

This is especially important when using animation libraries like GSAP, Anime.js, or the Web Animations API.

Task Checklist

  • When using :css="false", always call done() in @enter and @leave hooks
  • Call done() when your JavaScript animation completes (in the onComplete callback)
  • Remember: done() is optional when CSS handles the transition, but required with :css="false"
  • Use :css="false" to prevent CSS rules from interfering with JS animations

Problematic Code:

<template>
  <!-- BAD: No done() callback - animation is skipped! -->
  <Transition :css="false" @enter="onEnter" @leave="onLeave">
    <div v-if="show" class="box">Content</div>
  </Transition>
</template>

<script setup>
import gsap from 'gsap'

function onEnter(el) {
  // Animation starts but Vue doesn't wait for it!
  gsap.from(el, {
    opacity: 0,
    y: 50,
    duration: 0.5
  })
  // Missing done() call - element appears with no animation
}

function onLeave(el) {
  gsap.to(el, {
    opacity: 0,
    y: -50,
    duration: 0.5
  })
  // Missing done() call - element removed immediately!
}
</script>

Correct Code:

<template>
  <!-- GOOD: done() callback signals animation completion -->
  <Transition :css="false" @enter="onEnter" @leave="onLeave">
    <div v-if="show" class="box">Content</div>
  </Transition>
</template>

<script setup>
import gsap from 'gsap'

function onEnter(el, done) {
  gsap.from(el, {
    opacity: 0,
    y: 50,
    duration: 0.5,
    onComplete: done  // Tell Vue animation is complete
  })
}

function onLeave(el, done) {
  gsap.to(el, {
    opacity: 0,
    y: -50,
    duration: 0.5,
    onComplete: done  // Element removed after animation
  })
}
</script>

Why Use :css="false"?

  1. Prevents CSS interference: Vue won't add transition classes that might conflict
  2. Slight performance benefit: Skips CSS transition detection
  3. Clearer intent: Makes it explicit that JS controls the animation
<template>
  <!-- Without :css="false", Vue adds v-enter-active etc. classes -->
  <!-- These can interfere with your JS animation timing -->
  <Transition @enter="onEnter" @leave="onLeave">
    <div v-if="show">May have CSS conflicts</div>
  </Transition>

  <!-- With :css="false", no classes added - full JS control -->
  <Transition :css="false" @enter="onEnter" @leave="onLeave">
    <div v-if="show">Pure JS animation</div>
  </Transition>
</template>

Complete JavaScript Transition Example

<template>
  <Transition
    :css="false"
    @before-enter="onBeforeEnter"
    @enter="onEnter"
    @after-enter="onAfterEnter"
    @enter-cancelled="onEnterCancelled"
    @before-leave="onBeforeLeave"
    @leave="onLeave"
    @after-leave="onAfterLeave"
    @leave-cancelled="onLeaveCancelled"
  >
    <div v-if="show" class="animated-box">Content</div>
  </Transition>
</template>

<script setup>
import gsap from 'gsap'
import { ref } from 'vue'

const show = ref(false)
let enterAnimation = null
let leaveAnimation = null

function onBeforeEnter(el) {
  // Set initial state before animation
  el.style.opacity = 0
  el.style.transform = 'translateY(50px)'
}

function onEnter(el, done) {
  // Store animation reference for potential cancellation
  enterAnimation = gsap.to(el, {
    opacity: 1,
    y: 0,
    duration: 0.5,
    ease: 'power2.out',
    onComplete: done  // REQUIRED with :css="false"
  })
}

function onAfterEnter(el) {
  // Cleanup after enter completes
  enterAnimation = null
}

function onEnterCancelled() {
  // Handle interruption (e.g., user toggles quickly)
  if (enterAnimation) {
    enterAnimation.kill()
    enterAnimation = null
  }
}

function onBeforeLeave(el) {
  // Set state before leaving
}

function onLeave(el, done) {
  leaveAnimation = gsap.to(el, {
    opacity: 0,
    y: -50,
    duration: 0.5,
    ease: 'power2.in',
    onComplete: done  // REQUIRED with :css="false"
  })
}

function onAfterLeave(el) {
  leaveAnimation = null
}

function onLeaveCancelled() {
  if (leaveAnimation) {
    leaveAnimation.kill()
    leaveAnimation = null
  }
}
</script>

Using Web Animations API

<script setup>
function onEnter(el, done) {
  const animation = el.animate([
    { opacity: 0, transform: 'scale(0.9)' },
    { opacity: 1, transform: 'scale(1)' }
  ], {
    duration: 300,
    easing: 'ease-out'
  })

  animation.onfinish = done  // Call done when animation ends
}

function onLeave(el, done) {
  const animation = el.animate([
    { opacity: 1, transform: 'scale(1)' },
    { opacity: 0, transform: 'scale(0.9)' }
  ], {
    duration: 300,
    easing: 'ease-in'
  })

  animation.onfinish = done
}
</script>

Common Mistakes

// WRONG: Calling done() immediately instead of after animation
function onEnter(el, done) {
  gsap.from(el, { opacity: 0, duration: 0.5 })
  done() // Called immediately - animation skipped!
}

// WRONG: Forgetting done() parameter
function onEnter(el) {  // No 'done' parameter
  gsap.from(el, {
    opacity: 0,
    onComplete: done  // Error: done is not defined!
  })
}

// CORRECT: Pass done to animation callback
function onEnter(el, done) {
  gsap.from(el, {
    opacity: 0,
    duration: 0.5,
    onComplete: done  // Called after 0.5s
  })
}

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.