.agents/skills/vue-best-practices/references/plugins.md
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.
install() or as an install functionapp instance in install() to register components/directives/providesPlugin (and options tuple types when needed)InjectionKey<T>) for provide/inject in pluginsapp.use()A Vue plugin must be either:
install(app, options?)BAD:
const notAPlugin = {
doSomething() {}
}
app.use(notAPlugin)
GOOD:
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 })
}
}
app.use(myPlugin, { prefix: 'custom', debug: true })
GOOD:
import type { App } from 'vue'
function simplePlugin(app: App, options?: { message: string }) {
app.config.globalProperties.$greet = () => options?.message ?? 'Hello!'
}
app.use(simplePlugin, { message: 'Welcome!' })
install()Inside install(), wire behavior through Vue application APIs:
app.component() for global componentsapp.directive() for global directivesapp.provide() for injectable services and configapp.config.globalProperties for optional global helpers (sparingly)BAD:
const uselessPlugin = {
install(app, options) {
const service = createService(options)
}
}
GOOD:
const usefulPlugin = {
install(app, options) {
const service = createService(options)
app.provide(serviceKey, service)
}
}
Use Vue's Plugin type to keep install signatures and options type-safe.
import type { App, Plugin } from 'vue'
interface MyOptions {
apiKey: string
}
const myPlugin: Plugin<[MyOptions]> = {
install(app: App, options: MyOptions) {
app.provide(apiKeyKey, options.apiKey)
}
}
String keys can collide ('http', 'config', 'i18n'). Use symbol keys with InjectionKey<T> so injections are unique and typed.
BAD:
export default {
install(app) {
app.provide('http', axios)
app.provide('config', appConfig)
}
}
GOOD:
import type { AxiosInstance } from 'axios'
import type { InjectionKey } from 'vue'
interface AppConfig {
apiUrl: string
timeout: number
}
export const httpKey: InjectionKey<AxiosInstance> = Symbol('http')
export const configKey: InjectionKey<AppConfig> = Symbol('appConfig')
export default {
install(app) {
app.provide(httpKey, axios)
app.provide(configKey, { apiUrl: '/api', timeout: 5000 })
}
}
Wrap required injections in composables that throw clear setup errors.
import type { AuthService } from '@/injection-keys'
import { inject } from 'vue'
import { authKey } from '@/injection-keys'
export function useAuth(): AuthService {
const auth = inject(authKey)
if (!auth) {
throw new Error('Auth plugin not installed. Did you forget app.use(authPlugin)?')
}
return auth
}