Color mode
Light/dark/auto color mode with an SSR-safe, flash-free theme.
adminlte-vue supports light, dark, and auto color modes built on Bootstrap's data-bs-theme
attribute. The preference is persisted to localStorage and applied before first paint in Nuxt, so
there is no flash of the wrong theme on reload.
Overview
Color mode is provided by LteDashboardLayout, which calls provideColorMode() in its setup().
Descendant components read and update the mode through the useColorMode() composable. The state is:
colorMode— the user's preference:'light' | 'dark' | 'auto'.resolvedMode— the concrete mode applied to the page:'light' | 'dark'. WhencolorModeis'auto', it follows the systemprefers-color-schemesetting.
The resolved mode is written to data-bs-theme on the <html> element, and the preference is saved
under the lte-theme localStorage key.
Usage
The toggle component
LteColorModeToggle is a ready-made dropdown (rendered as a navbar <li>) that lets the user pick
Light, Dark, or Auto. It takes no props and must live inside LteDashboardLayout — typically in the
topbar's navbar list. In Nuxt it is auto-registered, so no import is needed.
<template>
<ul class="navbar-nav ms-auto">
<ClientOnly>
<LteColorModeToggle />
</ClientOnly>
</ul>
</template>
Wrapping the toggle in <ClientOnly> avoids a hydration mismatch on the trigger glyph, which
depends on the persisted preference resolved on the client.
Reading and setting the mode
Use useColorMode() anywhere inside the layout to read or change the mode programmatically.
<script setup lang="ts">
const { colorMode, resolvedMode, setColorMode } = useColorMode()
</script>
<template>
<div>
<p>Preference: {{ colorMode }} (resolved: {{ resolvedMode }})</p>
<button @click="setColorMode('light')">Light</button>
<button @click="setColorMode('dark')">Dark</button>
<button @click="setColorMode('auto')">Auto</button>
</div>
</template>
colorMode is a writable Ref, so colorMode.value = 'dark' works too — setColorMode() is just a
convenience wrapper.
useColorMode() API
useColorMode() injects the color-mode state provided by LteDashboardLayout. Calling it outside
the layout throws an error.
| Name | Type | Description |
|---|---|---|
colorMode | Ref<'light' | 'dark' | 'auto'> | The user's color-mode preference. Writable. |
resolvedMode | ComputedRef<'light' | 'dark'> | The concrete mode applied to the page; resolves 'auto' against the system setting. |
setColorMode | (mode: 'light' | 'dark' | 'auto') => void | Set the preference. Persists to localStorage and updates data-bs-theme. |
import { useColorMode } from 'adminlte-vue'
const { colorMode, resolvedMode, setColorMode } = useColorMode()
Setting the initial mode
The initial preference defaults to 'auto'. In Nuxt, set it through the module's defaults:
export default defineNuxtConfig({
modules: ['@adminlte/nuxt'],
adminlte: {
defaults: {
initialColorMode: 'auto', // 'light' | 'dark' | 'auto'
},
},
})
This value is used as the fallback by the blocking head script and seeds the layout's initial state.
A persisted lte-theme value always takes precedence once the page loads.
How the flash is avoided in Nuxt
A flash of the wrong theme happens when the saved preference is only applied after Vue hydrates. To
prevent it, @adminlte/nuxt injects a small blocking inline <head> script (keyed
lte-theme-init) that runs before first paint. It reads lte-theme from localStorage (falling
back to initialColorMode), resolves auto against prefers-color-scheme, and sets data-bs-theme
on <html> immediately:
(function () {
try {
var k = localStorage.getItem('lte-theme') || 'auto'
var d = k === 'auto'
? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
: k
document.documentElement.setAttribute('data-bs-theme', d)
} catch (e) {}
})()
After hydration, useColorMode() owns all reactive updates — it reconciles with the persisted
preference, listens for system prefers-color-scheme changes (while in auto), and re-applies
data-bs-theme whenever the mode changes.
The script is on by default. You can disable it with the themeScript module option (for example if
you inject your own):
export default defineNuxtConfig({
modules: ['@adminlte/nuxt'],
adminlte: {
themeScript: false,
},
})