Remix
@theme-kit/remix
Loader-based SSR theming with a blocking bootstrap and the same React/runtime adapter contract as the core React integration.
Installation
Install the package for your framework alongside @theme-kit/core.
pnpm add @theme-kit/remixQuick Start
Start from scratch — install the package, then wrap your app in the provider at the entry point shown below.
1import { ThemeProvider } from "@theme-kit/remix";
2import { themes } from "./themes";
3
4export default function App() {
5 return (
6 <ThemeProvider themes={themes} defaultTheme="mint-light">
7 <Outlet />
8 </ThemeProvider>
9 );
10}@theme-kit/core surface, so history, batching, snapshots, packs and lifecycle work the same way across frameworks.Implementation
Read and update theme state from any component using the framework-native primitives below.
1import { ThemeProvider } from "@theme-kit/remix";
2
3export default function App() {
4 return (
5 <ThemeProvider>
6 <Outlet />
7 </ThemeProvider>
8 );
9}What's Available
@theme-kit/remix ships 9 exports in 3 categories. Everything below is also documented in the full API reference. Click any export to reveal what it does and how to use it.
Server
Client
Transition
Use Cases
The important features in practice — copy any of these straight into your app.
Loader-driven initial theme
Resolve the theme on the server from the request, then hydrate with it.
1// app/root.tsx
2import { useLoaderData } from "@remix-run/react";
3import { ThemeProvider } from "@theme-kit/remix";
4import { getInitialThemeState } from "@theme-kit/remix/server";
5
6export async function loader({ request }: LoaderFunctionArgs) {
7 return { initial: await getInitialThemeState(request, { themes }) };
8}
9
10export default function App() {
11 const { initial } = useLoaderData();
12 return (
13 <ThemeProvider initial={initial}>
14 <Outlet />
15 </ThemeProvider>
16 );
17}Blocking script
Emit the blocking head script so there is no theme flash.
1import { Links, Scripts } from "@remix-run/react";
2import { ThemeHead } from "@theme-kit/remix";
3import { themes } from "./themes";
4
5export function Layout({ children }) {
6 return (
7 <html>
8 <head>
9 <ThemeHead themes={themes} defaultTheme="light" />
10 <Links />
11 </head>
12 <body>{children}<Scripts /></body>
13 </html>
14 );
15}Client switcher
All hooks are re-exported from @theme-kit/remix for client components.
1import { useTheme, useThemeHistory } from "@theme-kit/remix";
2
3export function ThemeControls() {
4 const { theme, toggleTheme } = useTheme();
5 const { undo, redo, canUndo } = useThemeHistory();
6 return (
7 <div>
8 <button onClick={toggleTheme}>{theme.name}</button>
9 <button onClick={undo} disabled={!canUndo}>Undo</button>
10 <button onClick={redo}>Redo</button>
11 </div>
12 );
13}Persistence
createRemixThemePersistence keeps the selection in sync with the server.
1import { createRemixThemePersistence } from "@theme-kit/remix";
2import { themes } from "./themes";
3
4export const persistence = createRemixThemePersistence(themes, "light", {
5 key: "theme",
6});Smooth theme transitions
Enable CSS transitions on theme changes for a polished user experience.
1// app/root.tsx
2import { ThemeProvider } from "@theme-kit/remix";
3
4export default function App() {
5 return (
6 <ThemeProvider
7 themes={themes}
8 defaultTheme="light"
9 transition={{ enabled: true, duration: 300, easing: "ease-in-out" }}
10 >
11 <Outlet />
12 </ThemeProvider>
13 );
14}More Examples
Scoped theming, history controls, and framework-specific patterns.
1// app/root.tsx
2import { useLoaderData } from "@remix-run/react";
3import { ThemeProvider, ThemeHead } from "@theme-kit/remix";
4import { getInitialThemeState } from "@theme-kit/remix/server";
5
6export async function loader({ request }: LoaderFunctionArgs) {
7 return { initial: await getInitialThemeState(request, { themes }) };
8}
9
10export default function App() {
11 const { initial } = useLoaderData<typeof loader>();
12 return (
13 <html lang="en">
14 <head>
15 <ThemeHead themes={themes} />
16 </head>
17 <body>
18 <ThemeProvider initial={initial}>
19 <Outlet />
20 </ThemeProvider>
21 </body>
22 </html>
23 );
24}API Reference
Server
| Export | Description |
|---|---|
Loader / server-side theming | Resolve the theme from cookies in a loader and render it before hydration. |
blocking-script.tsx | Blocking script that applies the persisted theme before first paint. |
createRemixThemePersistence() | Remix-flavored persistence adapter with cookie mirroring. |
computeFingerprint() | Fingerprint the theme config to reject stale cookies. |
Client
| Export | Description |
|---|---|
ThemeProvider | Client provider consuming the loader-resolved selection. |
Full hook set + ThemeScope | Every React hook plus scoped subtrees. |
Server entry helpers | Utilities under `@theme-kit/remix/server` for SSR wiring. |
Transition
| Export | Description |
|---|---|
transition prop | Pass `transition` to ThemeProvider to enable CSS transitions on theme changes. |
runtime.store.set(theme, { suppressTransition: true }) | Per-update escape hatch: `runtime.store.set(theme, { suppressTransition: true })` skips the configured animation. For custom animation orchestration, compose the core diff/plan/runner APIs. |