React Router
Framework mode (the Remix successor) with cookie storage and loader reading
Use cookie storage so the root loader can read the theme on the server:
import { ThemeProvider } from 'uni-themes'
import { ThemeScript } from 'uni-themes/script'
import { getThemeFromRequest } from 'uni-themes/server'
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'
import type { LoaderFunctionArgs } from 'react-router'
export function loader({ request }: LoaderFunctionArgs) {
return { theme: getThemeFromRequest(request) } // null until the user picks
}
export default function App() {
return (
<html lang="en" suppressHydrationWarning>
<head>
<meta charSet="utf-8" />
<ThemeScript storage="cookie" />
<Meta />
<Links />
</head>
<body>
<ThemeProvider storage="cookie">
<Outlet />
</ThemeProvider>
<ScrollRestoration />
<Scripts />
</body>
</html>
)
}Components use useTheme() from uni-themes as usual — no 'use client'
needed outside RSC frameworks.
The playground runs this end to end.