skip to content

App-Wide Plugins

A plugin is an object with install(app, options), or a bare function, run by app.use to register app-wide features. Interviewers ask candidates to write one and type what it adds.

part ofVue.jsoverview, primer and where to startread it →
on this pageshow

explore

questions

4

In Vue 3, what is a plugin, and what exactly happens when you call app.use(plugin, options)?

level: juniorimportance: must knowfreq 62%

answer

  1. object or bare function
  2. install receives the app
  3. extra arguments passed through
  4. remembered once per app
  5. returns the app

basics

~20 s

A Vue 3 plugin is an object with an install(app, options) method, or a bare function used as install. app.use calls it synchronously with the app and any extra arguments, installs it once per app, and returns the app.

solid answer

~30 s

A plugin is self-contained code that adds app-level features. It is either an object exposing `install(app, ...options)` or a plain function that Vue treats as the install function. `app.use(plugin, options)` calls it immediately with the app instance plus the extra arguments you passed, and the plugin then works through the app: `app.component`, `app.directive`, `app.provide` or `app.config.globalProperties`. Vue remembers installed plugins per app, so a second `use` of the same plugin on that app is skipped with a dev warning. `app.use` returns the app so calls chain, and they should all run before `app.mount()`.

code

ts · 21 lines
ts
import { createApp, type App } from 'vue'
import Root from './App.vue'
import BaseButton from './BaseButton.vue'

// object plugin
const uiKit = {
  install(app: App, options: { prefix: string }) {
    app.component(`${options.prefix}Button`, BaseButton)
  },
}

// function plugin: the function itself is install
function versionBanner(app: App) {
  app.provide('vueVersion', app.version)
}

createApp(Root)
  .use(uiKit, { prefix: 'Ui' })
  .use(versionBanner)
  .use(uiKit, { prefix: 'Other' }) // skipped: already installed on this app
  .mount('#app')

go deeper

for a junior

Recall the two plugin shapes, that install receives the app plus your options, and that every app.use call belongs before mount in the chain.

for a middle

Explain the per-app installed set, the duplicate-install warning, why install's return value is ignored, and what an install function typically registers.

for a senior

Show judgement about plugin side effects: async initialisation before mount, per-app isolation when several apps share a page, and keeping plugins free of hidden global state.

for a principal

Frame plugins as the app's composition root: decide which cross-cutting concerns deserve a plugin, and keep the install surface small enough to test and remove.

## What a plugin is In Vue 3 an **application instance** is created with `createApp(App)`. Everything that should apply to the whole app, rather than to one component, is registered on that instance. A **plugin** is a reusable bundle of such registrations. Vue accepts two shapes: - an **object plugin**: any object with an `install(app, ...options)` method; - a **function plugin**: a bare function `(app, ...options) => void`, which Vue calls as if it were `install`. There is no base class and nothing to extend. If the value passed to `app.use` is neither a function nor an object with an `install` function, Vue does nothing and, in development, warns: *A plugin must either be a function or an object with an "install" function.* ## What app.use does, step by step The implementation lives in `createApp` in `runtime-core`. When you call `app.use(plugin, ...options)`: 1. Vue checks a per-app set of already-installed plugins. If this exact plugin object or function is in it, the call is skipped and development builds warn *Plugin has already been applied to target app.* 2. Otherwise, if `plugin.install` is a function, Vue records the plugin and calls `plugin.install(app, ...options)`. 3. Otherwise, if the plugin itself is a function, Vue records it and calls `plugin(app, ...options)`. 4. Vue returns `app`, so `createApp(App).use(a).use(b, opts).mount('#app')` chains naturally. Two details follow from that code. The return value of `install` is **ignored**, so an `async install` is not awaited: `app.use` returns before its promise settles. And the installed set belongs to **one app**: the same plugin used on two different apps runs twice, once against each. ## What an install function usually registers | API used inside install | What it adds | Where it is reached | |---|---|---| | `app.component(name, comp)` | a globally registered component | any template in the app | | `app.directive(name, dir)` | a globally registered custom directive | any template in the app | | `app.provide(key, value)` | an app-level injectable value | `inject(key)` in any component of the app | | `app.config.globalProperties.$x = ...` | a property on every component instance | templates and Options API `this` | A plugin may do any combination of these, and it may read `app.version` to branch on the Vue version it is installed into. ## Timing: configure first, then mount Plugins are ordinary synchronous setup code, so **order matters**: - Call every `app.use` before `app.mount()`. The first render resolves global components and directives and reads global properties, so anything registered later misses that render. - Remember that `app.mount()` returns the **root component instance**, not the app. `createApp(App).mount('#app').use(plugin)` fails because the value returned by mount has no `use` method. - If a plugin needs asynchronous data before the first render, load it before calling `mount`, rather than hoping `app.use` waits. ## How this differs from Vue 2 In Vue 2, `Vue.use(plugin)` mutated the **global** Vue constructor, so every instance on the page received the plugin, and there was no way to undo it. Vue 3 replaced that with `app.use`, scoped to a single app instance. Two apps on one page can therefore install different plugins, or the same plugin with different options, without interfering with each other. ## Where plugins meet the rest of the app A plugin normally does not touch components directly. It writes into the app's **context**, the per-app record that holds registered components, directives, provides and config. Every component created under that app receives a reference to the same context, which is why one `app.use` call in `main.ts` is visible everywhere in the tree and nowhere outside it. Consequences worth being able to explain: - **Tests.** A component test that needs a plugin must install it on the test's own app; the `app.use` in `main.ts` is not involved, because the test builds a different app. - **Order between plugins.** Plugins run in the order of the `use` calls, so a plugin that depends on something another plugin registered must be installed after it. - **Options are captured at install.** The options passed to `app.use` configure the plugin for that app; mutating the object later matters only if the plugin reads it lazily. - **Registrations are named.** Registering a component or directive under a name already taken on the app overwrites the earlier one, and development builds warn about the duplicate. ## Common mistakes - Treating `app.use` as page-global: it only affects the app it is called on. - Expecting a second `app.use(samePlugin, newOptions)` to reconfigure the plugin: it is skipped. - Writing `async install()` and assuming the app waits for it. - Chaining `.use()` after `.mount()`. - Confusing Vue app plugins with bundler plugins, which run at build time and never see the app instance.

  • Does Vue 3 wait for an async install function before continuing?
    No. `app.use` calls `install` and discards its return value, so a returned promise is never awaited and `app.use` returns the app immediately. If data must be loaded before the first render, await it yourself and only then call `app.mount()`, or have the plugin expose a loading state that components render against.
  • Why does the same plugin run twice when two different apps each call app.use on it?
    The record of installed plugins is created inside each `createApp` call, so it is per app. A second app has an empty record and runs `install` again against its own context. That is the intended behaviour: registrations, provides and global properties live on each app separately.

app.use is like handing a contractor the keys to one apartment: they fit fixtures in that unit only, and the building log notes they have been there, so sending them to the same unit again does nothing.

saying these in an interview costs you the question

  • app.use installs the plugin for every Vue app on the page
  • Calling app.use twice with the same plugin registers everything twice
  • app.use awaits an async install before returning
  • A plugin must be an object; a bare function is rejected
  • app.use returns the root component instance, like mount does
open as a page

How would you write a small Vue 3 i18n plugin that offers a $t helper in templates and a provided API for script setup code?

level: middleimportance: should knowfreq 45%

basics

~20 s

Export an object whose install(app, options) keeps the locale in reactive state, assigns a $t function to app.config.globalProperties for templates, and calls app.provide with an InjectionKey so script setup code can inject the same typed API.

open as a page

In a Vue 3 TypeScript project, how do you type a $t property that a plugin adds through app.config.globalProperties?

level: middleimportance: should knowfreq 40%

basics

~20 s

Augment Vue's ComponentCustomProperties interface with declare module 'vue' in a file that is itself a module (has a top-level import or export), and make sure tsconfig includes it; templates and this then know $t's type.

open as a page

A Vue 3 plugin keeps its state in module-level variables and starts a timer in install; what goes wrong when several apps use it, and how do you fix it?

level: seniorimportance: should knowfreq 25%

basics

~20 s

Module state is shared by every app that installs the plugin, while install, and its timer, runs once per app and never stops. Create state per app inside install or a factory, and clear the timer in app.onUnmount (Vue 3.5+).

open as a page