---
title: Directive Best Practices
impact: MEDIUM
impactDescription: Custom directives are powerful but easy to misuse; following patterns prevents leaks, invalid usage, and unclear abstractions
type: best-practice
tags: [vue3, directives, custom-directives, composition, typescript]
---
# Directive Best Practices
**Impact: MEDIUM** - Directives are for low-level DOM access. Use them sparingly, keep them side-effect safe, and prefer components or composables when you need stateful or reusable UI behavior.
## Task List
- Use directives only when you need direct DOM access
- Do not mutate directive arguments or binding objects
- Clean up timers, listeners, and observers in `unmounted`
- Register directives in `
```
## Clean Up Side Effects in `unmounted`
Any timers, listeners, or observers must be removed to avoid leaks.
```ts
const vResize = {
mounted(el) {
const observer = new ResizeObserver(() => {})
observer.observe(el)
el._observer = observer
},
unmounted(el) {
el._observer?.disconnect()
}
}
```
## Prefer Function Shorthand for Single-Hook Directives
If you only need `mounted`/`updated`, use the function form.
```ts
const vAutofocus = (el) => el.focus()
```
## Use the `v-` Prefix and Script Setup Registration
```vue
```
## Type Custom Directives in TypeScript Projects
Use `Directive` so `binding.value` is typed, and augment Vue's template types so directives are recognized in SFC templates.
**BAD:**
```ts
// Untyped directive value and no template type augmentation
export const vHighlight = {
mounted(el, binding) {
el.style.backgroundColor = binding.value
}
}
```
**GOOD:**
```ts
import type { Directive } from 'vue'
type HighlightValue = string
export const vHighlight = {
mounted(el, binding) {
el.style.backgroundColor = binding.value
}
} satisfies Directive
declare module 'vue' {
interface ComponentCustomProperties {
vHighlight: typeof vHighlight
}
}
```
## Handle SSR with `getSSRProps`
Directive hooks such as `mounted` and `updated` do not run during SSR. If a directive sets attributes/classes that affect rendered HTML, provide an SSR equivalent via `getSSRProps` to avoid hydration mismatches.
**BAD:**
```ts
const vTooltip = {
mounted(el, binding) {
el.setAttribute('data-tooltip', binding.value)
el.classList.add('has-tooltip')
}
}
```
**GOOD:**
```ts
const vTooltip = {
mounted(el, binding) {
el.setAttribute('data-tooltip', binding.value)
el.classList.add('has-tooltip')
},
getSSRProps(binding) {
return {
'data-tooltip': binding.value,
class: 'has-tooltip'
}
}
}
```
## Prefer Declarative Templates When Possible
If a standard attribute or binding works, use it instead of a directive.
## Decide Between Directives and Components
Use a directive for DOM-level behavior. Use a component when behavior affects structure, state, or rendering.