useTheme
Theme management with multiple themes, CSS custom properties, and token alias resolution.
Installation
Install the Theme plugin in your app’s entry point:
import { createApp } from 'vue'
import { createThemePlugin } from '@vuetify/v0'
import App from './App.vue'
const app = createApp(App)
app.use(
createThemePlugin({
default: 'light',
themes: {
light: {
dark: false,
colors: {
primary: '#3b82f6',
},
},
dark: {
dark: true,
colors: {
primary: '#675496',
},
},
},
})
)
app.mount('#app')Usage
Once the plugin is installed, use the useTheme composable in any component:
<script setup lang="ts">
import { useTheme } from '@vuetify/v0'
const theme = useTheme()
function toggleTheme() {
theme.cycle(['light', 'dark'])
}
</script>
<template>
<div>
<h1>Current Theme: {{ theme.selectedId }}</h1>
<p>Dark mode: {{ theme.isDark ? 'enabled' : 'disabled' }}</p>
<button @click="toggleTheme">Toggle Theme</button>
</div>
</template>Adapters
Adapters let you swap the underlying CSS injection strategy without changing your application code.
| Adapter | Import | Description |
|---|---|---|
V0StyleSheetThemeAdapter | @vuetify/v0 | Injects CSS via adoptedStyleSheets (default, SPA only) |
V0UnheadThemeAdapter | @vuetify/v0/theme/adapters/unhead | Injects CSS via Unhead↗︎ for SSR/SSG |
Both adapters set a data-theme attribute on the root element. Theme CSS is scoped to [data-theme="light"] / [data-theme="dark"] selectors so multiple themes can coexist in the same stylesheet.
When to use each:
V0StyleSheetThemeAdapter(default) — SPAs without SSR. Usesdocument.adoptedStyleSheetsto inject a liveCSSStyleSheet— no DOM<style>element, zero flicker, works well with CSP when configured.V0UnheadThemeAdapter— SSR or SSG (Nuxt, VitePress). Manages the<style>tag anddata-themeattribute via Unhead so the correct theme is rendered in the initial HTML, avoiding a flash of the wrong theme on hydration. Requires@unhead/vue.
Architecture
useTheme extends createSingle for theme selection and createTokens for color resolution:
Reactivity
Theme selection and computed colors are reactive. Switching themes automatically updates CSS variables.
| Property/Method | Reactive | Notes |
|---|---|---|
selectedId | Current theme ID | |
selectedItem | Current theme ticket | |
selectedValue | Current theme colors | |
selectedIndex | Index in registry | |
colors | Resolved colors for all registered themes (keyed by theme ID) | |
isDark | Current theme is dark | |
select(id) | — | Switch to a specific theme by ID |
cycle(ids?) | — | Advance to the next theme. Pass an array to restrict which themes to cycle |
Examples
Palette tokens
FAQ
Use V0UnheadThemeAdapter (from @vuetify/v0/theme/adapters/unhead) — it renders the correct data-theme and <style> in the initial HTML via Unhead, avoiding a flash of the wrong theme on hydration. The default V0StyleSheetThemeAdapter is SPA-only.
Yes — theme.register({ id, dark, colors }) adds a theme after install (e.g. from user preferences). Registering an existing id is a no-op, so guard with theme.has(id) and fall through to theme.select(id).
useTheme manages the app-wide theme. Reach for the Theme provider when a subtree needs its own independent theme isolated from the rest of the app.
cycle() with no arguments advances through every registered theme in insertion order; pass an array — cycle(['light', 'dark']) — to restrict the rotation to a subset. Use select(id) to jump straight to a specific theme.
Register each theme’s colors as token aliases that reference shared palette tokens, resolved through createTokens. Changing one palette shade then propagates to every theme that references it, instead of editing each theme’s colors by hand.