Custom directives and plugins

Vue · Advanced · 11 min

Directives and plugins are how you extend Vue itself, and how most Vue libraries are installed.

Composables reuse stateful logic; custom directives reuse low-level DOM manipulation on plain elements. A directive is an object of lifecycle hooks: created, beforeMount, mounted, beforeUpdate, updated, beforeUnmount, unmounted. Each receives (el, binding, vnode), where binding.value is the expression's value, binding.arg is the argument (v-tooltip:top gives 'top') and binding.modifiers is an object of modifiers.

In <script setup>, any camelCase variable starting with v is usable as a directive: const vFocus = { mounted: (el) => el.focus() } enables <input v-focus>. A function shorthand runs on both mounted and updated. Register globally with app.directive('focus', vFocus).

Prefer components or composables when possible; directives are for things like focus, click-outside, intersection observers and tooltips. They do not work well on components with multiple root nodes.

A plugin is an object with install(app, options) (or just a function). Inside it you can:

  • register components and directives with app.component / app.directive,
  • make values available with app.provide(key, value),
  • add app.config.globalProperties.$x for template access (use sparingly),
  • set app.config.errorHandler or other app config.

Users install it with app.use(plugin, options). Vue Router, Pinia and i18n libraries are all plugins.

Example

// clickOutside.js
export const vClickOutside = {
  mounted(el, binding) {
    el._onDocClick = (e) => {
      if (!el.contains(e.target)) binding.value(e)   // call the handler
    }
    document.addEventListener('click', el._onDocClick)
  },
  unmounted(el) {
    document.removeEventListener('click', el._onDocClick)
  },
}

// plugin.js
import { vClickOutside } from './clickOutside.js'
export const UiKit = {
  install(app, { locale = 'en' } = {}) {
    app.directive('click-outside', vClickOutside)
    app.provide('ui-locale', locale)
  },
}

// main.js: createApp(App).use(UiKit, { locale: 'fr' }).mount('#app')
// usage:   <div v-click-outside="close" class="menu">...</div>

Remember

  • Directives are objects of hooks receiving (el, binding); use them for DOM-level behaviour
  • In script setup, vSomething variables are directives automatically
  • A plugin is install(app, options); it registers components, directives and provides

Watch out for

  • Forgetting to remove listeners in unmounted, leaking handlers for every mounted element.

Check yourself

  1. What is `binding.arg` for `<p v-pin:left.animate="200">`?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: vuejs.org/guide/reusability/custom-directives.html

Builds on

Unlocks

Nothing builds on this yet.