Files
stack/packages/mosaic/framework/skills/vue-best-practices/reference/plugin-structure-install-method.md
T
fargo 1a822493ba format: apply repo prettier (3.8.1) to the folded skills tree
963 markdown files reformatted with the repository's pinned prettier so
pnpm format:check covers the folded tree like every other repo file.

The formatter's embedded-language pass also normalized code fences
(TS semicolons, closed HTML tags in examples, lowercased CSS hex colors,
one renumbered list that skipped an index). Alphanumeric token deltas vs
the fold commit were audited file-by-file; all are formatter-equivalent
markup normalizations plus the four sanitized skills.
2026-08-19 14:37:17 -05:00

4.6 KiB

Plugin Structure: Install Method Requirements

Rule

A Vue plugin must be either an object with an install() method, or a function that serves as the install function. The install function receives the app instance and optional user-provided options.

Why This Matters

  1. API contract: Vue's app.use() expects a specific interface. Incorrect structure causes silent failures or errors.

  2. Options passing: The install method receives options that users pass to app.use(), enabling plugin configuration.

  3. App access: The install method receives the app instance, providing access to app.component(), app.directive(), app.provide(), etc.

Plugin Structures

// plugins/myPlugin.ts
import type { App } from 'vue';

interface PluginOptions {
  prefix?: string;
  debug?: boolean;
}

const myPlugin = {
  install(app: App, options: PluginOptions = {}) {
    const { prefix = 'my', debug = false } = options;

    if (debug) {
      console.log('Installing myPlugin with prefix:', prefix);
    }

    app.provide('myPlugin', { prefix });
  },
};

export default myPlugin;

// Usage
app.use(myPlugin, { prefix: 'custom', debug: true });

Function as install (alternative)

// plugins/simplePlugin.ts
import type { App } from 'vue';

function simplePlugin(app: App, options?: { message: string }) {
  app.config.globalProperties.$greet = () => {
    return options?.message ?? 'Hello!';
  };
}

export default simplePlugin;

// Usage
app.use(simplePlugin, { message: 'Welcome!' });

Factory function pattern (most flexible)

// plugins/configuredPlugin.ts
import type { App, Plugin } from 'vue';

interface I18nOptions {
  locale: string;
  messages: Record<string, Record<string, string>>;
  fallbackLocale?: string;
}

export function createI18n(options: I18nOptions): Plugin {
  return {
    install(app: App) {
      // Options are captured in closure - no need to pass through app.use()
      const { locale, messages, fallbackLocale = 'en' } = options;

      const translate = (key: string): string => {
        return messages[locale]?.[key] ?? messages[fallbackLocale]?.[key] ?? key;
      };

      app.provide('i18n', { translate, locale });
    },
  };
}

// Usage - options passed to factory, not app.use()
const i18n = createI18n({
  locale: 'fr',
  messages: {
    en: { hello: 'Hello' },
    fr: { hello: 'Bonjour' },
  },
});

app.use(i18n); // No second argument needed

Common Plugin Capabilities

const fullFeaturedPlugin = {
  install(app: App, options: PluginOptions) {
    // 1. Register global components
    app.component('MyButton', MyButtonComponent);
    app.component('MyInput', MyInputComponent);

    // 2. Register global directives
    app.directive('focus', focusDirective);

    // 3. Provide injectable values (recommended)
    app.provide('pluginConfig', options);

    // 4. Add global properties (use sparingly)
    app.config.globalProperties.$myHelper = helperFunction;

    // 5. Add global mixins (avoid if possible)
    app.mixin({
      created() {
        // Runs for every component
      },
    });

    // 6. Custom error handling
    app.config.errorHandler = (err, vm, info) => {
      // Handle errors
    };
  },
};

TypeScript: Plugin Type

Use the Plugin type for proper typing:

import type { App, Plugin } from 'vue';

// With options type parameter
interface MyOptions {
  apiKey: string;
}

const myPlugin: Plugin<[MyOptions]> = {
  install(app: App, options: MyOptions) {
    // options is typed as MyOptions
  },
};

// Without options
const simplePlugin: Plugin = {
  install(app: App) {
    // No options expected
  },
};

Common Mistakes

Missing install method

// BAD - This is just an object, not a plugin
const notAPlugin = {
  doSomething() {
    /* ... */
  },
};
app.use(notAPlugin); // Error or silent failure

// GOOD
const actualPlugin = {
  install(app) {
    app.provide('service', {
      doSomething() {
        /* ... */
      },
    });
  },
};

Forgetting to use the app parameter

// BAD - Does nothing
const uselessPlugin = {
  install(app, options) {
    const service = createService(options);
    // Forgot to register anything with app!
  },
};

// GOOD
const usefulPlugin = {
  install(app, options) {
    const service = createService(options);
    app.provide('service', service); // Actually makes it available
  },
};

References