Next.js
@theme-kit/next
App Router theming with SSR-safe hydration, cookie persistence, and zero flash of incorrect theme.
Installation
Install the package for your framework alongside @theme-kit/core.
pnpm add @theme-kit/nextQuick Start
Start from scratch — install the package, then wrap your app in the provider at the entry point shown below.
1// app/layout.tsx
2import { ThemeProvider } from "@theme-kit/next";
3import { themes } from "./theme/themes";
4
5export default function RootLayout({ children }) {
6 return (
7 <ThemeProvider
8 themes={themes}
9 defaultTheme="mint-light"
10 >
11 {children}
12 </ThemeProvider>
13 );
14}@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.
1// app/layout.tsx
2import { ThemeProvider } from "@theme-kit/next";
3import { themes } from "./theme/themes";
4
5export default function RootLayout({ children }) {
6 return (
7 <ThemeProvider themes={themes} defaultTheme="mint-light">
8 {children}
9 </ThemeProvider>
10 );
11}
12
13// components/ThemeSwitcher.tsx
14"use client";
15import { useTheme } from "@theme-kit/next/client";What's Available
@theme-kit/next ships 13 exports in 4 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
Scrollbar
Use Cases
The important features in practice — copy any of these straight into your app.
SSR provider with zero flash
The server component resolves the initial theme, renders CSS variables in the HTML, and blocks the flash.
1// app/layout.tsx
2import { ThemeProvider } from "@theme-kit/next";
3
4export default function RootLayout({ children }) {
5 return (
6 <ThemeProvider
7 themes={themes}
8 defaultTheme="system"
9 className="scroll-smooth"
10 body={{ className: "font-sans" }}
11 >
12 {children}
13 </ThemeProvider>
14 );
15}Client-side switcher
Hooks live behind @theme-kit/next/client and update cookies + DOM together.
1// app/theme-switcher.tsx
2"use client";
3import { useTheme } from "@theme-kit/next/client";
4
5export function ThemeSwitcher() {
6 const { theme, mode, setMode, toggleTheme } = useTheme();
7 return (
8 <button onClick={toggleTheme}>
9 {theme.name} · {mode}
10 </button>
11 );
12}Scoped theming in a client component
ThemeScope works in any client component, next to server-rendered content.
1// app/widgets.tsx
2"use client";
3import { ThemeScope, type ThemeTransitionOptions } from "@theme-kit/next/client";
4
5const transition: ThemeTransitionOptions = { duration: 300, easing: "ease" };
6
7export function Widget() {
8 return (
9 <ThemeScope theme="forest" transition={transition}>
10 <div className="rounded-xl p-4">Always forest here</div>
11 </ThemeScope>
12 );
13}Scheduled + multi-window sync
Every runtime capability is available to client components through hooks.
1// app/runtime-demo.tsx
2"use client";
3import { useEffect } from "react";
4import { useThemeRuntime, useThemeLifecycle } from "@theme-kit/next/client";
5
6export function RuntimeDemo() {
7 const runtime = useThemeRuntime();
8 const { on } = useThemeLifecycle();
9
10 useEffect(() => {
11 const off = on("beforeThemeChange", (e) => console.log(e.next.name));
12 return off;
13 }, [on]);
14
15 return (
16 <button onClick={() => runtime.update({ radius: { sm: 8, md: 12 } })}>
17 Soften corners
18 </button>
19 );
20}Smooth theme transitions
Enable CSS transitions on theme changes for a polished user experience.
1// app/layout.tsx
2import { ThemeProvider } from "@theme-kit/next";
3
4export default function RootLayout({ children }) {
5 return (
6 <ThemeProvider
7 themes={themes}
8 defaultTheme="light"
9 transition={{
10 enabled: true,
11 duration: 300,
12 easing: "ease-in-out",
13 }}
14 >
15 {children}
16 </ThemeProvider>
17 );
18}More Examples
Scoped theming, history controls, and framework-specific patterns.
1// app/layout.tsx
2import { ThemeProvider } from "@theme-kit/next";
3
4export default function RootLayout({ children }) {
5 return (
6 <ThemeProvider themes={themes} defaultTheme="mint-light"> {/* mint-light | mint-dark | light | dark | system */}
7 {children}
8 </ThemeProvider>
9 );
10}
11
12// app/theme-switcher.tsx
13"use client";
14import { useTheme } from "@theme-kit/next/client";
15
16export function ThemeSwitcher() {
17 const { theme, mode, setMode, toggleTheme } = useTheme();
18 return (
19 <div>
20 <button onClick={toggleTheme}>{theme.name} · {mode}</button>
21 <button onClick={() => setMode("dark")}>Dark</button>
22 <button onClick={() => setMode("light")}>Light</button>
23 </div>
24 );
25}API Reference
Server
| Export | Description |
|---|---|
ThemeProvider (Server Component) | Reads `theme-mode`, `theme-family`, `theme-fingerprint` cookies, validates the fingerprint, resolves the initial theme, and renders `<html data-theme>` with inline CSS variables before hydration. |
Blocking bootstrap script | Emits a blocking script in `<head>` that applies the persisted theme before first paint. |
Dark-mode CSS fallback | Emits `@media (prefers-color-scheme: dark)` styles when the persisted mode is `system`. |
createNextThemePersistence() | Mirrors selection to cookies (`theme-mode`, `theme-family`, `theme-name`, `theme-fingerprint`) so the server renders the right theme next request. |
scheduled prop | Pass `scheduled={{ lightTheme, darkTheme }}` to the server ThemeProvider to enable sunrise/sunset switching app-wide. Coordinates are optional — each visitor's timezone is auto-detected on the client. |
Client
| Export | Description |
|---|---|
ClientThemeProvider | Cookie + localStorage persistence, fingerprint check, and `.dark` class sync after hydration. |
@theme-kit/next/client | Re-exports every React hook plus ThemeScope, ThemeInspector and ThemeModeButton for client components. |
ThemeBootstrap | Injects SSR dark-mode CSS via `useServerInsertedHTML`. |
useThemeSchedule() | Reactive sunrise/sunset controller (via `@theme-kit/next/client`): `enabled`, `active`, `status`, `sunrise`, `sunset`, `nextTransition` plus `enable()`/`disable()`/`set()`. |
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 from `@theme-kit/next/client`: call `runtime.store.set(theme, { suppressTransition: true })` when a switch must be instantaneous. |
Scrollbar
| Export | Description |
|---|---|
scrollbar prop | Enables Theme Kit's custom overlay scrollbar. The SSR bootstrap hides the native scrollbar before first paint, then the overlay engine synchronizes with browser scrolling without replacing native scroll behavior. |
scrollbar option | Configure the overlay appearance from semantic tokens, including an explicit color token when needed. The overlay remains theme-aware and updates with runtime theme changes. |