@wrksz/themesv0.5.0

Why not next-themes?

A comparison of @wrksz/themes and next-themes.

Edit on GitHub

Last updated on

@wrksz/themes is a drop-in replacement for next-themes. It fixes every known bug and adds missing features. No API changes needed.

next-themes is slowly maintained - 43 open issues and 16 open PRs as of March 2026, with React 19 compatibility bugs still unresolved in the latest release.

Bug fixes

React 19 script warning

next-themes renders an inline <script> inside a Client Component, which triggers a React 19 warning. @wrksz/themes splits the provider into a Server Component (ThemeProvider) that renders the script, and a Client Component (ClientThemeProvider) that handles state.

__name minification bug

Production builds that minify function names break next-themes internals. Fixed.

Stale theme in React 19 Activity and cacheComponents

next-themes uses useState + useContext, which causes stale theme values when React's Activity or cacheComponents suspend and resume a subtree. @wrksz/themes uses useSyncExternalStore with a per-instance store, which always returns the latest value.

Missing features

Multiple classes per theme

Both libraries support space-separated values in the value map. The bug is in removal: next-themes removes only the top-level theme key as a single class, so switching from a multi-class theme leaves stale classes in the DOM.

@wrksz/themes flattens all mapped values before removing them:

for (const attr of attrs) {
  if (attr === "class") {
    const toRemove = themes.flatMap((t) =>
      (valueMap?.[t] ?? t).split(" "),
    );
    el.classList.remove(...toRemove);
    el.classList.add(...attrValue.split(" "));
  } else {
    el.setAttribute(attr, attrValue);
  }
}

This means switching from "dark high-contrast" to "light" correctly removes both dark and high-contrast.

Nested providers

next-themes uses a single global context - nested providers share state. @wrksz/themes creates a per-instance store so each provider is fully independent.

sessionStorage support

<ThemeProvider storage="sessionStorage">

Disable storage

<ThemeProvider storage="none">

meta theme-color support

Updates <meta name="theme-color"> automatically when the theme changes. Useful for Safari and PWAs:

<ThemeProvider themeColor={{ light: "#ffffff", dark: "#0a0a0a" }}>

Server-provided theme

Initialize the theme from a server-side source (database, session, cookie) on every mount:

<ThemeProvider
  initialTheme={userTheme}
  onThemeChange={saveUserTheme}
>

Generic types

const { theme, setTheme } = useTheme<"light" | "dark" | "high-contrast">();

Comparison table

next-themes@wrksz/themes
React 19 script warningFixed
__name minification bugFixed
React 19 Activity / cacheComponents stale themeFixed
Multiple classes per themeFixed
Nested providersYes
sessionStorage supportYes
Disable storageYes
meta theme-color supportYes
Server-provided themeYes
Generic typesYes
Zero runtime dependenciesYesYes

On this page