withModifiers helper for event modifiers
The withModifiers() helper from Vue applies event and key modifiers to event handlers in render functions. Usage: import { withModifiers } from 'vue'; h('div', { onClick: withModifiers(() => {}, ['self']) }). In JSX: <div onClick={withModifiers(() => {}, ['self'])} />.
Built-in components must be imported for render functions
Built-in components such as KeepAlive, Transition, TransitionGroup, Teleport, and Suspense must be imported for use in render functions: import { h, KeepAlive, Teleport, Transition, TransitionGroup } from 'vue'. Then use them like: h(Transition, { mode: 'out-in' }, /* ... */).
v-model in render functions
The v-model directive expands to modelValue and onUpdate:modelValue props. In render functions, provide these props manually: h(SomeComponent, { modelValue: props.modelValue, 'onUpdate:modelValue': (value) => emit('update:modelValue', value) }) in Composition API, or h(SomeComponent, { modelValue: this.modelValue, 'onUpdate:modelValue': (value) => this.$emit('update:modelValue', value) }) in Options API.
Custom directives with withDirectives
Custom directives are applied to vnodes using the withDirectives() helper: import { h, withDirectives } from 'vue'; const vnode = withDirectives(h('div'), [[pin, 200, 'top', { animate: true }]]). This applies the pin directive with value 200, argument 'top', and modifiers { animate: true }, equivalent to <div v-pin:top.animate="200"></div>.
resolveDirective() for dynamically registered directives
If a custom directive is registered by name and cannot be imported directly, use the resolveDirective() helper to resolve it programmatically in render functions.
Template refs with useTemplateRef in Composition API (Vue 3.5+)
In Composition API with Vue 3.5+, use useTemplateRef() to create template refs: import { h, useTemplateRef } from 'vue'; export default { setup() { const divEl = useTemplateRef('my-div'); return () => h('div', { ref: 'my-div' }) } }.
Template refs with ref() in Composition API (Vue 3.4 and earlier)
In Composition API versions before 3.5, create template refs by passing a ref() directly as a prop: import { h, ref } from 'vue'; export default { setup() { const divEl = ref(); return () => h('div', { ref: divEl }) } }.
Template refs with Options API
In Options API render functions, create template refs by passing the ref name as a string: export default { render() { return h('div', { ref: 'divEl' }) } }.
Custom directive definition with lifecycle hooks
A custom directive is defined as an object containing lifecycle hooks similar to those of a component. The hooks receive the element the directive is bound to. Custom directives are mainly intended for reusing logic that involves low-level DOM access on plain elements.
camelCase v-prefix naming in script setup
In <script setup>, any camelCase variable that starts with the 'v' prefix can be used as a custom directive. For example, vHighlight can be used in the template as v-highlight.
Local directive registration with directives option
Custom directives can be registered locally using the directives option in the component. When not using <script setup>, custom directives are registered via the directives option.
Global directive registration
Custom directives can be globally registered at the app level using app.directive('directiveName', { /* ... */ }). This makes the directive usable in all components.
When to use custom directives
Custom directives should only be used when the desired functionality can only be achieved via direct DOM manipulation. Declarative templating with built-in directives such as v-bind is recommended when possible because they are more efficient and server-rendering friendly.
Directive hooks lifecycle
A directive definition object can provide several hook functions (all optional): created (called before bound element's attributes or event listeners are applied), beforeMount (called right before the element is inserted into the DOM), mounted (called when the bound element's parent component and all its children are mounted), beforeUpdate (called before the parent component is updated), updated (called after the parent component and all of its children have updated), beforeUnmount (called before the parent component is unmounted), unmounted (called when the parent component is unmounted).
Directive hook arguments: el, binding, vnode
Directive hooks receive three arguments: el (the element the directive is bound to, can be used to directly manipulate the DOM), binding (an object containing value, oldValue, arg, modifiers, instance, and dir), and vnode (the underlying VNode representing the bound element). prevVnode is only available in beforeUpdate and updated hooks.
Directive binding object properties
The binding argument passed to directive hooks is an object containing: value (the value passed to the directive), oldValue (the previous value, only available in beforeUpdate and updated), arg (the argument passed to the directive), modifiers (an object containing modifiers), instance (the instance of the component where the directive is used), and dir (the directive definition object).
Directive binding object read-only properties
Apart from el, directive hook arguments should be treated as read-only and never modified. If you need to share information across hooks, it is recommended to do so through the element's dataset.
Dynamic directive arguments
Custom directive arguments can be dynamic. For example, v-example:[arg]="value" will have the directive argument reactively updated based on the arg property in component state.
Function shorthand for custom directives
When a custom directive has the same behavior for mounted and updated hooks with no need for other hooks, the directive can be defined as a function. This function will be called for both mounted and updated.
Function shorthand custom directive example
app.directive('color', (el, binding) => { el.style.color = binding.value }) - this function will be called for both mounted and updated hooks.
Custom directives with object literal values
Directives can take any valid JavaScript expression as a value, including object literals. For example, v-demo="{ color: 'white', text: 'hello!' }" passes an object to the directive.
Custom directives on components not recommended
Using custom directives on components is not recommended because unexpected behaviour may occur when a component has multiple root nodes. When used on components, custom directives will always apply to a component's root node.
Custom directives on multi-root components
When a custom directive is 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".
v-focus custom directive example
const vFocus = { mounted: (el) => el.focus() } enables focusing an element. This directive is more useful than the autofocus attribute because it works not just on page load but also when the element is dynamically inserted by Vue.
v-highlight custom directive example
const vHighlight = { mounted: (el) => { el.classList.add('is-highlight') } } is an example of a directive that adds a class to an element when it is inserted into the DOM by Vue.
Native DOM event handler typing
When handling native DOM events in TypeScript, explicitly annotate the event parameter to avoid implicit 'any' type. For example, `function handleChange(event: Event)`. You may need to use type assertions when accessing properties, such as `(event.target as HTMLInputElement).value`.
Global custom directives TypeScript typing
To get type hints and type checking for global custom directives declared with app.directive(), extend the GlobalDirectives interface from the 'vue' module. Define a directive type using Directive<Element, Value> and add it to GlobalDirectives with the v prefix.
Global directive type definition example
```ts
import type { Directive } from 'vue'
export type HighlightDirective = Directive<HTMLElement, string>
declare module 'vue' {
export interface GlobalDirectives {
vHighlight: HighlightDirective
}
}
export default {
mounted: (el, binding) => {
el.style.backgroundColor = binding.value
}
} satisfies HighlightDirective
```
This example shows defining a typed global directive and extending GlobalDirectives to register it.
Typed event handlers require explicit type annotations in Options API
When writing event handlers in Options API, the event argument implicitly has type 'any' without annotation, which causes TypeScript errors when strict mode or noImplicitAny is enabled. Explicitly annotate the event type, such as: handleChange(event: Event). Use type assertions when accessing properties: (event.target as HTMLInputElement).value
Example: Typing event handlers with proper type annotations
import { defineComponent } from 'vue'
export default defineComponent({
methods: {
handleChange(event: Event) {
console.log((event.target as HTMLInputElement).value)
}
}
})
Template supports TypeScript in binding expressions
The <template> also supports TypeScript in binding expressions when <script lang="ts"> or <script setup lang="ts"> is used. This is useful in cases where you need to perform type casting in template expressions.
Type casting in template with 'as' operator
Type casting can be performed in template expressions using the 'as' operator for inline type casting.
Template type casting example
<script setup lang="ts">
let x: string | number = 1
</script>
<template>
{{ (x as number).toFixed(2) }}
</template>