The Reusability chapters describe the three extension points Vue gives you and, more importantly, when each one is appropriate. The guide is explicit that composables are the default, directives are for low-level DOM access only, and plugins are for app-level installation. Knowing that hierarchy is a frequent design-question answer.
use; accept refs, getters or plain values and normalise them with toValue(); return a plain object of refs so callers can destructure; if a caller wants an object, they wrap it in reactive().onMounted, clean up in onUnmounted (or onScopeDispose); guard for SSR. The guide's useEventListener and useMouse examples show the pattern; useFetch shows reactive inputs with watchEffect refetching when a getter-based URL changes.<script setup>, setup(), or another composable, or in lifecycle hooks; the exception for <script setup> is that after await the instance context is restored automatically. app.runWithContext() lets you call them outside a component.useFeatureA(), useFeatureB().<script setup> any camelCase variable starting with v is usable as a directive (const vFocus = { mounted: el => el.focus() } → v-focus); otherwise the directives option or app.directive('focus', …).created, beforeMount, mounted, beforeUpdate, updated, beforeUnmount, unmounted, each receiving (el, binding, vnode, prevVnode).binding: value, oldValue (only in beforeUpdate/updated), arg (v-dir:arg), modifiers ({ foo: true }), instance, dir. Directives may receive object literals as values.mounted and updated.v-bind and built-ins.getSSRProps(binding, vnode) to render attributes on the server.install(app, options) or a function (app, options) => {}, installed with app.use(plugin, options).app.component/app.directive for globals, app.provide for injectable resources, app.config.globalProperties for this.$x in Options API, plus anything with a side effect (a router, a store).app.config.globalProperties.$translate reading dot paths from options, and app.provide('i18n', options) so Composition API code can inject('i18n').export function useEventListener<K extends keyof WindowEventMap>(
target: MaybeRefOrGetter<Window | HTMLElement | null>,
event: K,
handler: (e: WindowEventMap[K]) => void,
) {
// Mount/unmount pairing as the guide shows; toValue accepts a value, a ref or a getter.
onMounted(() => toValue(target)?.addEventListener(event, handler as EventListener))
onUnmounted(() => toValue(target)?.removeEventListener(event, handler as EventListener))
}
<script setup lang="ts">
import type { Directive } from 'vue'
// Object form with the binding fields the docs list. v-autofocus:soft.select="delayMs"
const vAutofocus: Directive<HTMLInputElement, number> = {
mounted(el, { value, arg, modifiers }) {
setTimeout(() => {
if (arg === 'soft' && document.activeElement !== document.body) return
el.focus()
if (modifiers.select) el.select()
}, value ?? 0)
},
getSSRProps: () => ({ 'data-autofocus': '' }), // rendered by the server
}
</script>
<template>
<input v-autofocus:soft.select="50">
</template>
import type { App } from 'vue'
export const i18nLite = {
install(app: App, options: Record<string, unknown>) {
app.config.globalProperties.$translate = (key: string) =>
key.split('.').reduce<unknown>((o, k) => (o as Record<string, unknown>)?.[k], options) as string
app.provide('i18n', options) // Composition API side: inject('i18n')
},
}
// app.use(i18nLite, { greetings: { hello: 'Ahoj' } })
<MyComponent v-tooltip="text"> applies the directive to the component's root element, silently, and if the component has multiple roots the directive is dropped with a warning. The guide recommends props for component behaviour and directives only for raw DOM work.
useMouse() from the guide with useEventListener, then make the target a ref to an element instead of window.v-if and record the order.$translate in an Options API component and inject('i18n') in a <script setup> one."The guide's hierarchy: a composable for any stateful logic that components share, because it is explicit, typed and testable; a custom directive only when the behaviour is inherently about manipulating a DOM element, like focusing, measuring or attaching a third-party widget, and even then not on components with several roots; a plugin when something has to be installed at the application level, such as registering global components, providing a resource with app.provide, or adding globalProperties for Options API code. Most 'I need a directive' cases are really a composable with a template ref."
Nothing yet. Notes filed here show up on this page and the section overview.
Components II — attrs, slots, provide/inject, async
Fallthrough attributes and inheritAttrs, slots in every form, provide/inject with reactivity and Symbol keys, and async components with loading states and lazy hydration strategies.
Built-in components
Transition and TransitionGroup classes, hooks and modes, KeepAlive include/exclude/max, Teleport targets and defer, and Suspense's async dependencies, events and nesting rules.