Plugins
v0 plugins are Vue plugins built with createPlugin. They provide app-wide singletons for features like theming, locale, and logging. For understanding the underlying architecture, see Core.
Using Built-in Plugins
Basic Installation
import { createApp } from 'vue'
import { createThemePlugin, createLocalePlugin } from '@vuetify/v0'
const app = createApp(App)
// Install plugins
app.use(createThemePlugin())
app.use(createLocalePlugin())
app.mount('#app')With Options
app.use(
createThemePlugin({
default: 'dark',
themes: {
light: { dark: false, colors: { primary: '#1976D2' } },
dark: { dark: true, colors: { primary: '#2196F3' } },
},
}),
)
app.use(
createLocalePlugin({
default: 'en',
messages: { en: { hello: 'Hello' } },
}),
)Available Plugins
| Plugin | Purpose | Composable |
|---|---|---|
createThemePlugin | CSS variable theming, dark mode | useTheme |
createLocalePlugin | i18n translations | useLocale |
createRtlPlugin | RTL direction support | useRtl |
createLoggerPlugin | Structured logging | useLogger |
createStoragePlugin | Reactive localStorage/sessionStorage | useStorage |
createPermissionsPlugin | Role-based access control | usePermissions |
createBreakpointsPlugin | Responsive breakpoint detection | useBreakpoints |
createHydrationPlugin | SSR hydration management | useHydration |
createFeaturesPlugin | Feature flags with adapter support | useFeatures |
createRulesPlugin | Validation rule aliases and Standard Schema | useRules |
createDatePlugin | Date utilities with adapter pattern | useDate |
createNotificationsPlugin | Notification lifecycle and toast queue | useNotifications |
createStackPlugin | Overlay z-index stacking | useStack |
All plugins are optional. Only install what you need—v0 works without any plugins installed.
Creating Custom Plugins
Using createPluginContext
createPluginContext is the recommended factory for new plugins. It generates the full [createXContext, createXPlugin, useX] tuple in a single call, eliminating the boilerplate of wiring together createContext and createPlugin manually.
import { createPluginContext } from '@vuetify/v0'
interface AnalyticsContext {
track: (event: string, data?: Record<string, unknown>) => void
identify: (userId: string) => void
}
interface AnalyticsOptions {
apiKey: string
debug?: boolean
}
export const [createAnalyticsContext, createAnalyticsPlugin, useAnalytics] =
createPluginContext<AnalyticsOptions, AnalyticsContext>(
'my:analytics',
({ apiKey, debug = false }) => ({
track: (event, data) => {
if (debug) console.log('Track:', event, data)
// send to analytics service using apiKey
},
identify: (userId) => {
// identify user
},
}),
)app.use(createAnalyticsPlugin({ apiKey: 'xxx', debug: true }))<script setup>
import { useAnalytics } from './plugins/analytics'
const analytics = useAnalytics()
analytics.track('page_view')
</script>createPluginContext also supports persist/restore lifecycle hooks for automatically saving and rehydrating plugin state across page loads. See createPlugin for details.
Manual Approach
For fine-grained control over the plugin lifecycle, use createContext and createPlugin directly:
import { createContext, createPlugin } from '@vuetify/v0'
interface AnalyticsContext {
track: (event: string, data?: Record<string, unknown>) => void
identify: (userId: string) => void
}
// 1. Create the context for DI
const [useAnalytics, provideAnalytics] = createContext<AnalyticsContext>('my:analytics')
// 2. Create the plugin factory
export function createAnalyticsPlugin() {
const context: AnalyticsContext = {
track: (event, data) => console.log('Track:', event, data),
identify: (userId) => console.log('Identify:', userId),
}
return createPlugin({
namespace: 'my:analytics',
provide: (app) => provideAnalytics(context, app),
})
}
export { useAnalytics }Plugin with Options
interface AnalyticsOptions {
apiKey: string
debug?: boolean
}
export function createAnalyticsPlugin(options: AnalyticsOptions) {
const { apiKey, debug = false } = options
const context: AnalyticsContext = {
track: (event, data) => {
if (debug) console.log('Track:', event, data)
// Send to analytics service
},
identify: (userId) => {
// Identify user
},
}
return createPlugin({
namespace: 'my:analytics',
provide: (app) => provideAnalytics(context, app),
})
}
// Usage
app.use(createAnalyticsPlugin({ apiKey: 'xxx', debug: true }))Plugin with Adapter Pattern
For extensible plugins that support multiple backends:
interface LoggerAdapter {
log: (level: string, message: string) => void
}
interface LoggerContext {
info: (msg: string) => void
warn: (msg: string) => void
error: (msg: string) => void
}
interface LoggerOptions {
adapter?: LoggerAdapter
}
const consoleAdapter: LoggerAdapter = {
log: (level, message) => console[level](message),
}
const [useLogger, provideLogger] = createContext<LoggerContext>('my:logger')
export function createLoggerPlugin(options: LoggerOptions = {}) {
const adapter = options.adapter ?? consoleAdapter
const context: LoggerContext = {
info: (msg) => adapter.log('info', msg),
warn: (msg) => adapter.log('warn', msg),
error: (msg) => adapter.log('error', msg),
}
return createPlugin({
namespace: 'my:logger',
provide: (app) => provideLogger(context, app),
})
}
// Custom adapter example
const sentryAdapter: LoggerAdapter = {
log: (level, message) => {
if (level === 'error') Sentry.captureMessage(message)
},
}
app.use(createLoggerPlugin({ adapter: sentryAdapter }))Consuming Plugins
<script setup>
import { useTheme, useLocale } from '@vuetify/v0'
const theme = useTheme()
const locale = useLocale()
// Theme API
theme.cycle() // Cycle through themes
theme.select('dark') // Select specific theme
theme.selectedItem.value // Current theme ticket
theme.selectedId.value // 'light' | 'dark'
theme.isDark.value // boolean
// Locale API
locale.t('hello')
locale.selectedItem.value // Current locale ticket
locale.selectedId.value // 'en'
</script>Plugin vs Context
| Need | Use |
|---|---|
| App-wide singleton (one instance) | Plugin with app.use() |
| Multiple instances per subtree | Context with provideContext |
| Configurable at install time | Plugin |
| Configurable per component tree | Context |
Best Practices
1. Provide Default Adapters
// Always provide a sensible default
const defaultAdapter = { /* ... */ }
const adapter = options.adapter ?? defaultAdapter2. Type the Context Interface
// Define what consumers get
interface ThemeContext {
selectedId: ComputedRef<string>
select: (id: string) => void
cycle: () => void
}3. Handle Missing Installation
const theme = useTheme()
// useTheme throws if createThemePlugin isn't installed
// This is intentional - fail fast