next-themes has 22 million weekly downloads. It hasn't had a release in over a year. There are 43 open issues and 16 unmerged PRs sitting there, including React 19 compatibility bugs that affect every project upgrading to Next.js 16.
I got tired of waiting. So I built @wrksz/themes.
The problem with next-themes
When Next.js 16 and React 19 dropped, projects started hitting a wall. The most common issues:
React 19 script warning - next-themes renders an inline <script> inside a Client Component. React 19 started warning about this explicitly:
Encountered a script tag while rendering React component.
Scripts inside React components are never executed when rendering on the client.
Stale theme with cacheComponents - next-themes uses useState + useContext, which causes stale theme values when React's Activity or cacheComponents suspend and resume a subtree. Your theme gets stuck.
__name minification bug - production builds that minify function names break next-themes internals silently.
Multi-class themes leave stale classes - if you map a theme to multiple CSS classes like "dark high-contrast", switching away from it leaves high-contrast stuck on the DOM forever.
All of these have been sitting open for months with no movement.
What I built
@wrksz/themes is a drop-in replacement. Migrating is literally one import change:
npm uninstall next-themes
npm install @wrksz/themes
// before
import { ThemeProvider } from "next-themes";
// after
import { ThemeProvider } from "@wrksz/themes/next";
That's it. The API is identical.
What's fixed
Every known bug from next-themes is fixed:
- React 19 script warning - fixed by using
useServerInsertedHTMLto inject the script outside the React component tree - Stale theme with cacheComponents - fixed by using
useSyncExternalStorewith a per-instance store -
__nameminification bug - fixed - Multi-class theme removal - fixed with proper
flatMap+splitbefore removing classes
What's new
Beyond fixes, I added features that were frequently requested but never implemented:
Cookie storage with zero-flash SSR - the biggest one. With storage="cookie", the Next.js provider reads the cookie server-side automatically. No flash, no suppressHydrationWarning hacks, no boilerplate:
// app/layout.tsx
import { ThemeProvider } from "@wrksz/themes/next";
export default async function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<ThemeProvider storage="cookie" defaultTheme="dark">
{children}
</ThemeProvider>
</body>
</html>
);
}
Generic types - full TypeScript safety with your custom theme names:
type AppTheme = "light" | "dark" | "high-contrast";
const { theme, setTheme } = useTheme<AppTheme>();
// theme: AppTheme | "system" | undefined
// setTheme: (theme: AppTheme | "system") => void
Nested providers - each provider gets its own independent store. You can scope themes to specific sections of your app:
<ThemeProvider forcedTheme="dark" target="#landing" storage="none">
<div id="landing">...</div>
</ThemeProvider>
ThemedImage - a built-in component that solves the hydration mismatch problem with theme-aware images:
import { ThemedImage } from "@wrksz/themes/client";
<ThemedImage
src={{ light: "/logo-light.png", dark: "/logo-dark.png" }}
alt="Logo"
width={200}
height={50}
/>
useThemeValue - returns a value from a map based on the current theme:
const label = useThemeValue({ light: "Switch to dark", dark: "Switch to light" });
sessionStorage, storage: "none" - more storage options for different use cases.
meta theme-color - updates <meta name="theme-color"> automatically for Safari and PWAs.
Comparison table
| next-themes | @wrksz/themes | |
|---|---|---|
| React 19 script warning | Yes | Fixed |
__name minification bug |
Yes | Fixed |
| Stale theme with cacheComponents | Yes | Fixed |
| Multi-class removal bug | Yes | Fixed |
| Nested providers | No | Yes |
| sessionStorage support | No | Yes |
| Cookie storage (zero-flash SSR) | No | Yes |
| Disable storage | No | Yes |
| meta theme-color | No | Yes |
| Server-provided theme | No | Yes |
| Generic TypeScript types | No | Yes |
| Zero runtime dependencies | Yes | Yes |
Try it
bun add @wrksz/themes
# or
npm install @wrksz/themes
Docs: themes.wrksz.dev
GitHub: github.com/jakubwarkusz/themes
Migration guide: themes.wrksz.dev/docs/migration
Top comments (0)