Next.js
App Router setup with a server-rendered ThemeScript
// stays a server component
import { ThemeProvider } from 'uni-themes'
import { ThemeScript } from 'uni-themes/script'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<ThemeScript />
</head>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
)
}'use client'
import { useTheme } from 'uni-themes'
export function ThemeSwitcher() {
const { resolvedTheme, setTheme } = useTheme()
return (
<button
onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}
>
Toggle
</button>
)
}Reading the theme on the server
Switch on cookie storage and use uni-themes/next:
<ThemeScript storage="cookie" />
// ...
<ThemeProvider storage="cookie">{children}</ThemeProvider>import { getTheme } from 'uni-themes/next'
// in a server component, route handler or server action
const theme = await getTheme() // 'dark' | 'light' | 'system' | nullgetTheme() returns null for first-time visitors — the inline script falls
back to the OS preference on the client.
Using with fumadocs
fumadocs' RootProvider bundles its own next-themes integration; disable it
and let uni-themes own the <html> class (this site does exactly that):
<RootProvider theme={{ enabled: false }}>{children}</RootProvider>