React
@theme-kit/react
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.
pnpm add @theme-kit/reactQuick Start
Start from scratch — install the package, then wrap your app in the provider at the entry point shown below.
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);@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, 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.
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.
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.
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.
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.
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.
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
| Export | Description |
|---|---|
ThemeProvider | Creates a runtime, wires DOM + CSS-variable bindings, and provides it via context. Accepts every ThemeRuntimeOptions prop. |
Runtime injection | Pass `runtime` to share an existing instance, or `initial` to seed the server-resolved selection for hydration. |
scheduled prop | Pass `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
| Export | Description |
|---|---|
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
| Export | Description |
|---|---|
ThemeScope | Apply a specific theme to a subtree; emits scoped CSS vars plus Tailwind-compatible `--color-*` / `--radius-*` variables. |
ThemeModeButton | One-click light → dark → system cycle button. |
ThemeInspector | Floating 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
| Export | Description |
|---|---|
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 adapters | Shadcn, Bootstrap, DaisyUI, and Open Props adapters can be registered directly with the runtime; their factories do not require React. |
Transition
| Export | Description |
|---|---|
transition prop | Built-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. |