Implement Dark and Light Theme in Next.js 16 with @wrksz/themes
A complete dark and light theme implementation in Next.js 16 using @wrksz/themes, React 19 and Tailwind CSS v4.
Dark and Light Theme in Next.js 16 with @wrksz/themes
Modern applications should support comfortable experiences in both light and dark environments.
Theme handling looks simple from the outside, but a complete implementation requires managing theme state, system preferences, persistence, and hydration problems.
In a previous article, we implemented a theme system manually without dependencies. In this article, we will use @wrksz/themes to create a cleaner production-ready solution.
What We Will Build
The final system supports:
- Light mode
- Dark mode
- System preference
- Theme persistence
- Tailwind CSS integration
- SSR-safe rendering
The flow:
User changes theme
|
v
@wrksz/themes manages state
|
v
HTML theme class changes
|
v
CSS variables update
|
v
Application UI updatesProject Stack
- Next.js 16 App Router
- React 19
- TypeScript
- Tailwind CSS v4
- @wrksz/themes
Installation
bun add lucide-react @wrksz/themesAdding ThemeProvider
import { ThemeProvider } from '@wrksz/themes/next';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
);
}Creating Theme Toggle
'use client';
import { useTheme } from '@wrksz/themes/client';
import { Moon, Sun } from 'lucide-react';
export function ThemeToggle() {
const { resolvedTheme, setTheme } = useTheme();
const isDark = resolvedTheme === 'dark';
return (
<button
onClick={() => setTheme(isDark ? 'light' : 'dark')}
className="hover:bg-muted rounded-full border p-3 transition"
aria-label="Theme toggle"
>
{isDark ? <Moon size={20} /> : <Sun size={20} />}
</button>
);
}Available Themes
The application supports:
- light
- dark
- system
System mode follows:
prefers-color-schemeTailwind CSS Integration
The active theme is applied through the HTML class:
<html class="dark"></html>CSS variables can then define theme colors:
:root {
--background: #ffffff;
}
.dark {
--background: #10131c;
}Components use semantic colors:
<div className="bg-background text-foreground">Content</div>Hydration Handling
Theme systems can cause hydration mismatches because the server does not know the user's browser preference.
A server render and client render may initially differ.
@wrksz/themes provides Next.js integration to simplify this process.
Native Implementation vs @wrksz/themes
A native implementation is useful for learning:
- React Context
- localStorage
- matchMedia
- DOM manipulation
- hydration handling
Using @wrksz/themes provides:
- Less boilerplate
- Cleaner API
- Easier maintenance
- Production-friendly implementation
Conclusion
Understanding how themes work internally is important.
However, production applications often benefit from using a dedicated solution.
@wrksz/themes provides a simple and maintainable way to add light, dark, and system themes to modern Next.js applications.

