SSR & Nuxt
Vue Ecosphere is SSR-safe: no component touches window, document, or localStorage at module scope. The package can be imported and rendered on the server without guards.
Nuxt 3
Create a plugin:
// plugins/ecosphere.ts
import Ecosphere from "vue-ecosphere";
import "vue-ecosphere/styles";
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(Ecosphere, {
size: "md",
hue: "primary",
});
});Add the package to transpile if you're on an older bundler:
// nuxt.config.ts
export default defineNuxtConfig({
build: {
transpile: ["vue-ecosphere"],
},
});Avoiding hydration flash
The library reads --ep-* variables from CSS, so theme color is correct on the first paint. For dark mode specifically, the dark class needs to be on <html> before hydration. The simplest pattern:
// app.vue
useHead({
htmlAttrs: { class: () => (isDark.value ? "dark" : "") },
});For cookie-driven preference, set it in a middleware so the SSR'd HTML already has the right class.
Vite / Vitest
For unit tests that mount components, use jsdom:
// vitest.config.ts
export default defineConfig({
test: {
environment: "jsdom",
},
});The library's own test suite uses jsdom 25 + @vue/test-utils 2.4 and runs without polyfills.
Astro
Use the Vue integration:
import vue from "@astrojs/vue";
export default defineConfig({ integrations: [vue()] });Then in a .astro or .vue island, import from vue-ecosphere as usual. Stylesheet must be imported once in your layout.
Common pitfalls
- Forgetting
import "vue-ecosphere/styles"— components render but look unstyled. The stylesheet is not auto-injected. - Conditional theme class on
<body>instead of<html>— the:root.darkselector targets<html>; mismatched targets break dark mode. window.matchMediaat setup time — never do this in your own wrapper; useonMountedor auseMediaQuerycomposable.
