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-shallowref-for-dynamic-components.md

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

Use shallowRef for Dynamic Component References

Impact: MEDIUM - When storing Vue components in reactive state for dynamic rendering, using ref() or reactive() causes Vue warnings and unnecessary reactivity overhead. Use shallowRef() instead.

Task Checklist

  • Use shallowRef for storing component references
  • Use markRaw when storing components in reactive objects
  • Avoid wrapping component definitions with deep reactivity
  • Check console for "[Vue warn]: Vue received a Component that was made a reactive object"

The Problem

Vue components are objects with internal properties that should not be made reactive. When you store a component in ref() or reactive(), Vue traverses all properties deeply, which:

  1. Triggers a console warning
  2. Creates unnecessary reactive proxies
  3. Can cause subtle bugs with component identity
  4. Impacts performance

Incorrect - Using ref() for components:

import { ref } from 'vue'
import ComponentA from './ComponentA.vue'
import ComponentB from './ComponentB.vue'

// BAD: Vue will warn about making component reactive
const currentComponent = ref(ComponentA)

function switchComponent() {
  currentComponent.value = ComponentB
}

Console warning:

[Vue warn]: Vue received a Component that was made a reactive object.
This can lead to unnecessary performance overhead and should be avoided
by marking the component with `markRaw` or using `shallowRef` instead of `ref`.

Solution 1: Use shallowRef

shallowRef only makes the .value reference reactive, not the contents:

import { shallowRef, type Component } from 'vue'
import ComponentA from './ComponentA.vue'
import ComponentB from './ComponentB.vue'

// CORRECT: shallowRef doesn't deep-proxy the component
const currentComponent = shallowRef<Component>(ComponentA)

function switchComponent() {
  currentComponent.value = ComponentB
}
<template>
  <component :is="currentComponent" />
</template>

Solution 2: Use markRaw in Reactive Objects

When components are part of a larger reactive object:

import { reactive, markRaw, type Component } from 'vue'
import TabHome from './TabHome.vue'
import TabProfile from './TabProfile.vue'
import TabSettings from './TabSettings.vue'

interface Tab {
  name: string
  component: Component
}

// CORRECT: markRaw prevents reactivity on component objects
const tabs = reactive<Tab[]>([
  { name: 'Home', component: markRaw(TabHome) },
  { name: 'Profile', component: markRaw(TabProfile) },
  { name: 'Settings', component: markRaw(TabSettings) }
])

const activeTab = shallowRef<Tab>(tabs[0])
<template>
  <div class="tabs">
    <button
      v-for="tab in tabs"
      :key="tab.name"
      @click="activeTab = tab"
    >
      {{ tab.name }}
    </button>
  </div>
  <component :is="activeTab.component" />
</template>

TypeScript Typing

For proper TypeScript support with dynamic components:

import { shallowRef, type Component, type DefineComponent } from 'vue'

// Generic component type
const currentComponent = shallowRef<Component | null>(null)

// Or more specific with props
interface MyComponentProps {
  title: string
}

const currentComponent = shallowRef<DefineComponent<MyComponentProps> | null>(null)

Dynamic Import with shallowRef

When using dynamic imports for code splitting:

import { shallowRef, defineAsyncComponent, type Component } from 'vue'

const currentComponent = shallowRef<Component | null>(null)

async function loadComponent(name: string) {
  const component = defineAsyncComponent(
    () => import(`./components/${name}.vue`)
  )
  currentComponent.value = component
}

Component Registry Pattern

For tab systems or wizard-like interfaces:

import { shallowRef, markRaw, type Component } from 'vue'

// Type-safe component registry
const componentRegistry = {
  home: markRaw(defineAsyncComponent(() => import('./Home.vue'))),
  about: markRaw(defineAsyncComponent(() => import('./About.vue'))),
  contact: markRaw(defineAsyncComponent(() => import('./Contact.vue')))
} as const

type ComponentKey = keyof typeof componentRegistry

const currentView = shallowRef<ComponentKey>('home')

// Computed to get current component
const currentComponent = computed(() => componentRegistry[currentView.value])
<template>
  <component :is="currentComponent" />
</template>

When to Use Each Approach

Scenario Solution
Single dynamic component reference shallowRef
Component in reactive array/object markRaw on component
Component map/registry markRaw each component
Async components defineAsyncComponent + shallowRef

Common Mistakes

Mistake 1: Using computed with ref

// BAD: Still triggers warning
const components = ref([ComponentA, ComponentB])
const current = computed(() => components.value[index.value])

// GOOD: Use shallowRef for the array
const components = shallowRef([ComponentA, ComponentB])

Mistake 2: Forgetting markRaw in map

// BAD: Components in map become reactive
const routes = reactive(new Map([
  ['home', HomeComponent],
  ['about', AboutComponent]
]))

// GOOD: Mark each component as raw
const routes = reactive(new Map([
  ['home', markRaw(HomeComponent)],
  ['about', markRaw(AboutComponent)]
]))

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.