Upgrading
Migrating from 1.x to 2.0
2.0.0 enforces the dependency-isolation contract. That removed public exports from twelve packages, so the upgrade is a real one — but every change is a moved import path or one extra argument, and this page covers all of them.
The removed symbols are not deprecated aliases — they cannot be re-exported from their old locations without restoring a dependency the release gate forbids. There is no shim and no codemod. If you import a Themvora adapter binding from a framework package or from an adapter root, that import will break.
1Which packages changed
| Package | Version | Breaking change |
|---|---|---|
| @themvora/astro | 2.0.0 | React surface moved to /client; ./adapters removed |
| @themvora/next | 2.0.0 | ./client no longer exports the adapter providers |
| @themvora/remix | 2.0.0 | root no longer exports the adapter providers |
| @themvora/nuxt | 2.0.0 | root no longer exports the four adapter composables |
| @themvora/vue | 2.0.0 | root no longer exports the adapter composables |
| @themvora/svelte | 2.0.0 | root no longer exports the adapter composables |
| @themvora/solid | 2.0.0 | root no longer exports the adapter composables |
| @themvora/shadcn | 2.0.0 | root no longer exports useShadcnTheme |
| @themvora/bootstrap | 2.0.0 | root no longer exports useBootstrapTheme |
| @themvora/daisyui | 2.0.0 | root no longer exports useDaisyTheme |
| @themvora/open-props | 2.0.0 | root no longer exports useOpenPropsTheme |
| @themvora/angular | 2.0.0 | root no longer exports the inject* adapter hooks |
| @themvora/core | 1.4.0 | none — additive (diagnostics, config) |
| @themvora/web | 1.4.0 | none — additive |
| @themvora/react | 1.3.1 | none — type-only |
| adapters, cli, devtools, mantine, mui, chakra, antd, tailwind, unocss | 1.3.0 | unchanged in this release |
2Adapter bindings moved to per-framework subpaths
Each adapter package (shadcn, bootstrap, daisyui, open-props) no longer exports its hook from the package root, and no longer depends on @themvora/react. It gains one subpath per framework instead.
import { useShadcnTheme } from "@themvora/shadcn"; // React
import { useShadcnTheme } from "@themvora/vue"; // Vue
import { useShadcnTheme } from "@themvora/next/client"; // Next
import { useShadcnTheme } from "@themvora/remix"; // Remiximport { useShadcnTheme } from "@themvora/shadcn/react"; // React
import { useShadcnTheme } from "@themvora/shadcn/vue"; // Vue
import { useShadcnTheme } from "@themvora/shadcn/react"; // Next
import { useShadcnTheme } from "@themvora/shadcn/react"; // RemixThe same shape applies to useBootstrapTheme, useDaisyTheme and useOpenPropsTheme. Each adapter exposes /react, /vue, /svelte, /solid and /angular.
The isolation contract permits an adapter to depend only on core/adapters. The adapter roots violated it by importing @themvora/react, and the framework packages violated it by hard-depending on eight to eleven adapters each — which is why @themvora/vue used to install React. A deprecated alias would reintroduce exactly those edges, so a shim is not possible.
3The runtime is now the first argument
useShadcnTheme();
useShadcnTheme({ strategy: "exact" });// React / Vue
const runtime = useThemeRuntime();
useShadcnTheme(runtime);
useShadcnTheme(runtime, { strategy: "exact" });
// Svelte
const runtime = getThemeRuntime();
useShadcnTheme(runtime);The runtime is no longer read from framework context inside the adapter — that indirection is precisely what forced the adapter to depend on the framework. Callers pass it from their own framework's accessor.
4Astro: root is framework-neutral
import { ThemeProviderClient, ThemeScope, useTheme } from "@themvora/astro";
import { useShadcnTheme } from "@themvora/astro/adapters";import { ThemeProviderClient, ThemeScope, useTheme } from "@themvora/astro/client";
import { useShadcnTheme } from "@themvora/shadcn/react";The React surface now lives behind the opt-in @themvora/astro/client subpath — the only entry that depends on React. If you render in Astro without React, use ThemeToggle.astro or getThemeController() instead of the hooks; that path needs no client framework at all.
ThemeScrollbar and ThemeInspector did not move to /client
These two are the one part of the old root surface with no /client equivalent. Astro's root now re-exports the web-component forms — ThemvoraScrollbar and ThemvoraInspector — from @themvora/web, and ships ./ThemeScrollbar.astro / ./ThemeInspector.astro subpaths. The React components named ThemeScrollbar / ThemeInspector still exist, unchanged, in @themvora/react.
5Behaviour changes to be aware of
setFamily() ignores an unregistered family
Previously it applied the family and then degraded to themes[0], while data-theme-selection-family kept naming a family that was not on screen — so the readout contradicted the applied theme. It is now a no-op. A caller passing an unregistered family has a bug; switching the visitor to a different family would have hidden it.
A "system" selection needs systemModeCSSTemplate
Expressing a "system" selection by inlining the resolved variables plus a dark media block never worked: an inline style on <html> outranks every stylesheet rule, so the dark block never applied and an OS-dark visitor kept the light colors. Emit systemModeCSSTemplate(light, dark) as a <style> element instead. Only "system" may use media queries — a concrete mode must be a plain :root rule.
createThemeRuntime() no longer requires an argument
This is a relaxation — createThemeRuntime() used to throw, so the form the docs show failed at runtime. Existing calls still compile.
6What did not break
- Persisted selections. The fingerprint algorithm is unchanged, so cookies and localStorage entries written by 1.x stay valid.
- The cookie contract. Names are unchanged (theme-name, theme-family, theme-mode, theme-fingerprint).
- The CLI exit-code contract. 0 OK / 1 error / 2 usage / 3 validation-failed — unchanged.
- Plugin lifecycle. No hook was removed; createDevToolsPlugin only gained methods.
- React lifecycle typing. useThemeLifecycle handlers narrowed from unknown to the per-event payload. A handler accepting unknown is still assignable, so previously-compiling code still compiles.
7Upgrade checklist
- Search for adapter imports coming from a framework package or an adapter root: useShadcnTheme, useBootstrapTheme, useDaisyTheme, useOpenPropsTheme, and the *ThemeProvider components. Repoint each to the adapter's per-framework subpath.
- Add the runtime as the first argument at every call site, taken from your framework's accessor.
- If you use Astro, move the React surface to @themvora/astro/client and replace any @themvora/astro/adapters import.
- If you inline a "system" selection, switch to systemModeCSSTemplate.
- If you call setFamily() with a value that may not be registered, validate it first — the call is now a no-op.
Upgrading a theme definition rather than a package? See Migration for migrateTheme() and schema versions.