Files
agent-skills/skills/vue-best-practices/reference/directive-avoid-on-components.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

150 lines
3.6 KiB
Markdown

---
title: Avoid Using Custom Directives on Components
impact: HIGH
impactDescription: Custom directives on multi-root components are silently ignored, causing unexpected behavior
type: gotcha
tags: [vue3, directives, components, multi-root, best-practices]
---
# Avoid Using Custom Directives on Components
**Impact: HIGH** - Using custom directives on components is not recommended and can lead to unexpected behavior. When applied to a multi-root component, the directive will be ignored and a warning will be thrown. Unlike attributes, directives cannot be passed to a different element with `v-bind="$attrs"`.
Custom directives are designed for direct DOM manipulation on native HTML elements, not Vue components.
## Task Checklist
- [ ] Only apply custom directives to native HTML elements, not components
- [ ] If a component needs directive-like behavior, consider making it part of the component's API
- [ ] For components, use props and events instead of directives
- [ ] If you must use a directive on a component, ensure it has a single root element
**Incorrect:**
```vue
<template>
<!-- WRONG: Directive on a component - may be ignored -->
<MyComponent v-focus />
<!-- WRONG: Multi-root component - directive is ignored with warning -->
<MultiRootComponent v-highlight />
</template>
<script setup>
import MyComponent from './MyComponent.vue'
import MultiRootComponent from './MultiRootComponent.vue'
// MultiRootComponent.vue has:
// <template>
// <div>First root</div>
// <div>Second root</div>
// </template>
</script>
```
**Correct:**
```vue
<template>
<!-- CORRECT: Directive on native HTML element -->
<input v-focus />
<!-- CORRECT: Use props/events for component behavior -->
<MyComponent :should-focus="true" />
<!-- CORRECT: Wrap component in element if directive is needed -->
<div v-highlight>
<MyComponent />
</div>
</template>
<script setup>
import MyComponent from './MyComponent.vue'
</script>
```
## When a Directive on Component Works
Directives only work reliably on components with a **single root element**. The directive applies to the root node, similar to fallthrough attributes:
```vue
<!-- SingleRootComponent.vue -->
<template>
<div>I am the only root</div>
</template>
<!-- Parent.vue -->
<template>
<!-- This works because SingleRootComponent has one root -->
<SingleRootComponent v-my-directive />
</template>
```
However, this is still not recommended because:
1. It's fragile - refactoring to multi-root breaks the directive silently
2. It's unclear which element receives the directive
3. The component author may not expect external DOM manipulation
## Better Patterns
### Option 1: Component Prop
```vue
<!-- FocusableInput.vue -->
<template>
<input ref="inputRef" v-bind="$attrs" />
</template>
<script setup>
import { ref, onMounted, watch } from 'vue'
const props = defineProps({
autofocus: Boolean
})
const inputRef = ref(null)
onMounted(() => {
if (props.autofocus) {
inputRef.value?.focus()
}
})
</script>
<!-- Usage -->
<FocusableInput autofocus />
```
### Option 2: Exposed Method
```vue
<!-- FocusableInput.vue -->
<template>
<input ref="inputRef" />
</template>
<script setup>
import { ref } from 'vue'
const inputRef = ref(null)
const focus = () => inputRef.value?.focus()
defineExpose({ focus })
</script>
<!-- Parent.vue -->
<template>
<FocusableInput ref="myInput" />
</template>
<script setup>
import { ref, onMounted } from 'vue'
const myInput = ref(null)
onMounted(() => {
myInput.value?.focus()
})
</script>
```
## Reference
- [Vue.js Custom Directives - Usage on Components](https://vuejs.org/guide/reusability/custom-directives#usage-on-components)