Skip to content

Remix

@theme-kit/remix

SSRLoaders

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.

bash
pnpm add @theme-kit/remix

Quick Start

Start from scratch — install the package, then wrap your app in the provider at the entry point shown below.

app/root.tsx
tsx
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}
Every integration re-exports the full @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.

app/root.tsx
tsx
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.

Loader-driven initial theme
tsx
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.

Blocking script
tsx
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.

Client switcher
tsx
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.

Persistence
ts
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.

Smooth theme transitions
tsx
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.

app/root.tsx
tsx
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

ExportDescription
Loader / server-side themingResolve the theme from cookies in a loader and render it before hydration.
blocking-script.tsxBlocking 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

ExportDescription
ThemeProviderClient provider consuming the loader-resolved selection.
Full hook set + ThemeScopeEvery React hook plus scoped subtrees.
Server entry helpersUtilities under `@theme-kit/remix/server` for SSR wiring.

Transition

ExportDescription
transition propPass `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.
Remix — Theme Kit