Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Enforce Vue 3 best practices—Composition API, script setup, TypeScript, component boundaries, and reactivity patterns
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/plugins.md
1---2title: Vue Plugin Best Practices3impact: MEDIUM4impactDescription: Incorrect plugin structure or injection key strategy causes install failures, collisions, and unsafe APIs5type: best-practice6tags: [vue3, plugins, provide-inject, typescript, dependency-injection]7---89# Vue Plugin Best Practices1011**Impact: MEDIUM** - Vue plugins should follow the `app.use()` contract, expose explicit capabilities, and use collision-safe injection keys. This keeps plugin setup predictable and composable across large apps.1213## Task List1415- Export plugins as an object with `install()` or as an install function16- Use the `app` instance in `install()` to register components/directives/provides17- Type plugin APIs with `Plugin` (and options tuple types when needed)18- Use symbol keys (prefer `InjectionKey<T>`) for `provide/inject` in plugins19- Add a small typed composable wrapper for required injections to fail fast2021## Structure Plugins for `app.use()`2223A Vue plugin must be either:24- An object with `install(app, options?)`25- A function with the same signature2627**BAD:**28```ts29const notAPlugin = {30doSomething() {}31}3233app.use(notAPlugin)34```3536**GOOD:**37```ts38import type { App } from 'vue'3940interface PluginOptions {41prefix?: string42debug?: boolean43}4445const myPlugin = {46install(app: App, options: PluginOptions = {}) {47const { prefix = 'my', debug = false } = options4849if (debug) {50console.log('Installing myPlugin with prefix:', prefix)51}5253app.provide('myPlugin', { prefix })54}55}5657app.use(myPlugin, { prefix: 'custom', debug: true })58```5960**GOOD:**61```ts62import type { App } from 'vue'6364function simplePlugin(app: App, options?: { message: string }) {65app.config.globalProperties.$greet = () => options?.message ?? 'Hello!'66}6768app.use(simplePlugin, { message: 'Welcome!' })69```7071## Register Capabilities Explicitly in `install()`7273Inside `install()`, wire behavior through Vue application APIs:74- `app.component()` for global components75- `app.directive()` for global directives76- `app.provide()` for injectable services and config77- `app.config.globalProperties` for optional global helpers (sparingly)7879**BAD:**80```ts81const uselessPlugin = {82install(app, options) {83const service = createService(options)84}85}86```8788**GOOD:**89```ts90const usefulPlugin = {91install(app, options) {92const service = createService(options)93app.provide(serviceKey, service)94}95}96```9798## Type Plugin Contracts99100Use Vue's `Plugin` type to keep install signatures and options type-safe.101102```ts103import type { App, Plugin } from 'vue'104105interface MyOptions {106apiKey: string107}108109const myPlugin: Plugin<[MyOptions]> = {110install(app: App, options: MyOptions) {111app.provide(apiKeyKey, options.apiKey)112}113}114```115116## Use Symbol Injection Keys in Plugins117118String keys can collide (`'http'`, `'config'`, `'i18n'`). Use symbol keys with `InjectionKey<T>` so injections are unique and typed.119120**BAD:**121```ts122export default {123install(app) {124app.provide('http', axios)125app.provide('config', appConfig)126}127}128```129130**GOOD:**131```ts132import type { InjectionKey } from 'vue'133import type { AxiosInstance } from 'axios'134135interface AppConfig {136apiUrl: string137timeout: number138}139140export const httpKey: InjectionKey<AxiosInstance> = Symbol('http')141export const configKey: InjectionKey<AppConfig> = Symbol('appConfig')142143export default {144install(app) {145app.provide(httpKey, axios)146app.provide(configKey, { apiUrl: '/api', timeout: 5000 })147}148}149```150151## Provide Required Injection Helpers152153Wrap required injections in composables that throw clear setup errors.154155```ts156import { inject } from 'vue'157import { authKey, type AuthService } from '@/injection-keys'158159export function useAuth(): AuthService {160const auth = inject(authKey)161if (!auth) {162throw new Error('Auth plugin not installed. Did you forget app.use(authPlugin)?')163}164return auth165}166```167