Skip to content

React

@theme-kit/react

SPAViteContext

Provider + hooks for React 18/19. The reference integration, and the base that Next.js and Remix re-export.

Installation

Install the package for your framework alongside @theme-kit/core.

bash
pnpm add @theme-kit/react

Quick Start

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

main.tsx
tsx
1// main.tsx
2import { createRoot } from "react-dom/client";
3import { ThemeProvider } from "@theme-kit/react";
4import App from "./App";
5import { themes } from "./themes";
6
7createRoot(document.getElementById("root")!).render(
8  <ThemeProvider themes={themes} defaultTheme="mint-light">
9    <App />
10  </ThemeProvider>,
11);
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.tsx
tsx
1import { ThemeProvider, useTheme } from "@theme-kit/react";
2
3export function App() {
4  return (
5    <ThemeProvider themes={themes} transition={{ enabled: true }}>
6      <ThemeSwitcher />
7    </ThemeProvider>
8  );
9}
10
11function ThemeSwitcher() {
12  const { theme, family, setFamily, toggleTheme } = useTheme();
13  return (
14    <button onClick={toggleTheme}>
15      {theme.name} · {family}
16    </button>
17  );
18}

What's Available

@theme-kit/react ships 27 exports in 5 categories. Everything below is also documented in the full API reference. Click any export to reveal what it does and how to use it.

Provider

Hooks

Components

Library adapters

Transition

Use Cases

The important features in practice — copy any of these straight into your app.

Toggle light / dark

Read the active theme, mode and family and flip between them with zero config.

Toggle light / dark
tsx
1import { useTheme } from "@theme-kit/react";
2
3export function ThemeToggle() {
4  const { theme, mode, setMode, setFamily, toggleTheme } = useTheme();
5  return (
6    <div className="row">
7      <button onClick={toggleTheme}>{theme.name}</button>
8      <button onClick={() => setMode("dark")}>Dark</button>
9      <button onClick={() => setMode("light")}>Light</button>
10      <select value={family} onChange={(e) => setFamily(e.target.value)}>
11        {["neutral", "mint", "plum"].map((f) => (
12          <option key={f} value={f}>{f}</option>
13        ))}
14      </select>
15      <span className="mono">{mode}</span>
16    </div>
17  );
18}

Undo / redo + live token editing

Theme changes are snapshotted automatically. Undo, redo, or patch tokens at runtime.

Undo / redo + live token editing
tsx
1import { useThemeHistory, useThemeRuntime } from "@theme-kit/react";
2
3export function ThemeControls() {
4  const { undo, redo, canUndo, canRedo } = useThemeHistory();
5  const runtime = useThemeRuntime();
6
7  return (
8    <div>
9      <button onClick={undo} disabled={!canUndo}>Undo</button>
10      <button onClick={redo} disabled={!canRedo}>Redo</button>
11      <button
12        onClick={() => runtime.update({ colors: { primary: "#6366f1" } })}
13      >
14        Make primary indigo
15      </button>
16    </div>
17  );
18}

Scope a subtree

Apply a specific theme to a section of the tree with scoped CSS variables.

Scope a subtree
tsx
1import { ThemeScope, useScopedTheme, useRef, type ThemeTransitionOptions } from "@theme-kit/react";
2
3const transition: ThemeTransitionOptions = { duration: 300, easing: "ease" };
4
5export function Dashboard() {
6  return (
7    <>
8      <Sidebar /> {/* inherits the global theme */}
9      <ThemeScope theme="forest" transition={transition}>
10        <DataViz /> {/* always themed "forest" */}
11      </ThemeScope>
12    </>
13  );
14}
15
16export function ScopedCard() {
17  const ref = useRef<HTMLDivElement>(null);
18  useScopedTheme(ref, "forest");
19  return <div ref={ref}>Imperatively scoped</div>;
20}

Lifecycle events + theme packs

React to every theme change, and install a ready-made pack (e.g. a11y profiles) at runtime.

Lifecycle events + theme packs
tsx
1import { useEffect } from "react";
2import { useThemeLifecycle, useThemePacks } from "@theme-kit/react";
3
4export function Telemetry() {
5  const { on } = useThemeLifecycle();
6  const usePack = useThemePacks();
7
8  useEffect(() => {
9    const off = on("beforeThemeChange", (e) => {
10      console.log("theme changed to", e.next.name);
11    });
12    return off;
13  }, [on]);
14
15  return (
16    <button onClick={() => usePack({ name: "a11y", themes: highContrast })}
17    >
18      Apply High Contrast pack
19    </button>
20  );
21}

Smooth theme transitions

Enable CSS transitions on theme changes for a polished user experience.

Smooth theme transitions
tsx
1import { ThemeProvider } from "@theme-kit/react";
2
3export function App() {
4  return (
5    <ThemeProvider
6      themes={themes}
7      transition={{
8        enabled: true,
9        duration: 300,
10        easing: "ease-in-out",
11        properties: [
12          "color",
13          "background-color",
14          "border-color",
15          "border-radius",
16          "font-size",
17          "box-shadow",
18        ],
19      }}
20    >
21      <ThemeSwitcher />
22    </ThemeProvider>
23  );
24}

More Examples

Scoped theming, history controls, and framework-specific patterns.

theme-scope.tsx
tsx
1import { ThemeProvider, ThemeScope, useThemeHistory, type ThemeTransitionOptions } from "@theme-kit/react";
2
3const scopeTransition: ThemeTransitionOptions = { duration: 300, easing: "ease" };
4
5export function App() {
6  return (
7    <ThemeProvider themes={themes}>
8      <ThemeScope theme="forest" transition={scopeTransition}>
9        <PremiumPanel />
10      </ThemeScope>
11      <HistoryControls />
12    </ThemeProvider>
13  );
14}

API Reference

Provider

ExportDescription
ThemeProviderCreates a runtime, wires DOM + CSS-variable bindings, and provides it via context. Accepts every ThemeRuntimeOptions prop.
Runtime injectionPass `runtime` to share an existing instance, or `initial` to seed the server-resolved selection for hydration.
scheduled propPass `scheduled={{ lightTheme, darkTheme }}` to ThemeProvider to switch the app between light and dark at each visitor's local sunrise/sunset. Latitude/longitude are optional — the location is auto-detected from the browser timezone (or pin it with `timeZone`).

Hooks

ExportDescription
useTheme()Returns `{ theme, mode, family, setMode, setFamily, toggleTheme }`.
useThemeValue() / useThemeTokens()The active theme definition, and the active theme tokens.
useThemeMode() / useThemeFamily()Granular reads for the current mode and family.
useSetThemeMode() / useSetThemeFamily()Granular setters — set mode or family independently.
useToggleTheme()Toggle function for light/dark.
useThemeRuntime()Access the full runtime: registry, history, lifecycle, plugins.
useThemeHistory()`{ undo, redo, canUndo, canRedo, clear }`.
useThemeBatch()Wrap `runtime.batch()` for atomic, coalesced updates.
useThemeSnapshot() / useThemeRestore()Serialize and restore the full runtime state.
useThemeTimeTravel()`{ history, jump }` — indexed navigation through time.
useThemeLifecycle()Subscribe to typed lifecycle events (`beforeThemeChange`, `afterPersist`, ...).
useThemePacks()Install a theme pack at runtime via `runtime.use()`.
useThemeSchedule()Reactive sunrise/sunset controller: `enabled`, `active`, `status`, `sunrise`, `sunset`, `nextTransition` plus `enable()`/`disable()`/`set()`. Returns `null` when the provider has no `scheduled` option.

Components

ExportDescription
ThemeScopeApply a specific theme to a subtree; emits scoped CSS vars plus Tailwind-compatible `--color-*` / `--radius-*` variables.
ThemeModeButtonOne-click light → dark → system cycle button.
ThemeInspectorFloating dev panel: active theme, selection, flattened tokens, generated CSS variables.
useScopedTheme(ref, themeName)Imperative scoping for any element ref.
useThemePacks()Install a theme pack at runtime via `runtime.use()`.

Library adapters

ExportDescription
createMuiAdapter() / createChakraAdapter()Maps Theme Kit semantic tokens into native generated themes while preserving the framework-neutral runtime contract.
createAntdAdapter() / createMuiAdapter() / createChakraAdapter()Rebuilds the library theme from the active Theme Kit theme and exposes a reactive snapshot/subscribe bridge for React providers.
MantineThemeProvider + createMantineTheme()Mantine's generated-theme bridge: `createMantineTheme(runtime)` rebuilds the native Mantine theme from Theme Kit tokens, and `MantineThemeProvider` forces the color scheme to match the active mode.
CSS-variable adaptersShadcn, Bootstrap, DaisyUI, and Open Props adapters can be registered directly with the runtime; their factories do not require React.

Transition

ExportDescription
transition propBuilt-in runtime transition support. Configure duration/easing once on the provider; Theme Kit generates the transition styles at runtime, so applications do not need to maintain theme-transition rules in global CSS.
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.
React — Theme Kit