Last active
February 28, 2026 07:56
-
-
Save tommie/819457f75a950dfb975e4694524a91ba to your computer and use it in GitHub Desktop.
Nuxt color-mode v3 workaround for SSR preference
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Writes the resolved color mode value (dark/light) to a cookie so the | |
| // server can use it during SSR. The built-in color-mode module only stores | |
| // the preference ("system", "dark", "light"), but when preference is | |
| // "system" the server can't resolve it — it doesn't have a media query. | |
| // This cookie bridges that gap. | |
| const COOKIE_NAME = 'nuxt-color-mode-value' | |
| const PREF_COOKIE_NAME = 'nuxt-color-mode' | |
| const MAX_AGE = 365 * 24 * 60 * 60 // 1 year in seconds | |
| function setCookie(name: string, value: string) { | |
| document.cookie = `${name}=${value}; path=/; max-age=${MAX_AGE}; SameSite=Lax` | |
| } | |
| export default defineNuxtPlugin(() => { | |
| const colorMode = useColorMode() | |
| watch(() => colorMode.value, (value) => { | |
| setCookie(COOKIE_NAME, value) | |
| }, { immediate: true }) | |
| // TODO: Remove once Nuxt UI supports @nuxtjs/color-mode v4, which | |
| // has cookieAttrs. v3 sets a session cookie with no max-age. | |
| watch(() => colorMode.preference, (pref) => { | |
| setCookie(PREF_COOKIE_NAME, pref) | |
| }, { immediate: true }) | |
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Reads the resolved color mode value from the cookie written by the | |
| // client plugin. When preference is "system", the built-in color-mode | |
| // module can't resolve it on the server — there's no media query. | |
| // This plugin reads the resolved value and applies the CSS class to | |
| // <html> during SSR so hydration matches the client. | |
| // | |
| // User plugins run after module plugins (like color-mode), so the | |
| // colorMode state is already initialized when this runs. | |
| const COOKIE_NAME = 'nuxt-color-mode-value' | |
| export default defineNuxtPlugin(() => { | |
| const colorMode = useColorMode() | |
| const cookie = useCookie(COOKIE_NAME) | |
| if (cookie.value && colorMode.unknown) { | |
| colorMode.value = cookie.value | |
| // The color-mode module's htmlAttrs object isn't reactive, so | |
| // setting colorMode.value alone doesn't add the class to <html>. | |
| useHead({ | |
| htmlAttrs: { | |
| class: cookie.value | |
| } | |
| }) | |
| } | |
| }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment