@wrksz/themesv2.1.1

Getting Started

Install and configure @wrksz/themes in your Next.js app.

Note

These docs cover 2.x. Install @wrksz/themes without a prerelease tag. See Upgrading from 1.x.

Try it

A nested scoped to this card with storage="none", so it never touches the page theme. Two of the themes are custom.

theme …resolved …

Ada Lovelace

Commented on Reader view

2m

The sepia palette reads well at night. Could we ship it as the default for the reader view?

DesignReader view
Write a reply…Reply
theme-picker.tsx
"use client";

import { ClientThemeProvider, useTheme } from "@wrksz/themes/client";

const themes = ["light", "dark", "sepia", "forest"] as const;

export function ThemePreview({ children }: { children: React.ReactNode }) {
  return (
    <ClientThemeProvider
      themes={themes}
      defaultTheme="system"
      attribute="data-theme"
      target="#preview"
      storage="none"
    >
      <Picker />
      <div id="preview">{children}</div>
    </ClientThemeProvider>
  );
}

function Picker() {
  const { theme, setTheme } = useTheme<(typeof themes)[number]>();

  return [...themes, "system" as const].map((name) => (
    <button key={name} aria-pressed={theme === name} onClick={() => setTheme(name)}>
      {name}
    </button>
  ));
}
globals.css
#preview { --bg: #fcfcfc; --fg: #1f1f1f; --accent: #1f1f1f; }
#preview[data-theme="dark"] { --bg: #141414; --fg: #f2f2f2; --accent: #f2f2f2; }
#preview[data-theme="sepia"] { --bg: #f4ecd8; --fg: #433422; --accent: #8a5a2b; }
#preview[data-theme="forest"] { --bg: #1d2620; --fg: #e3ead9; --accent: #9cc797; }

#preview {
  background: var(--bg);
  color: var(--fg);
}

Installation

$pnpm add @wrksz/themes

Setup

Add to your root layout. Import from @wrksz/themes/next for Next.js - this avoids the React 19 inline script warning by using useServerInsertedHTML. Add suppressHydrationWarning to <html> to prevent hydration mismatches.

app/layout.tsx
import { ThemeProvider } from "@wrksz/themes/next";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  );
}

Usage

Use the hook in any Client Component to read and update the current theme.

components/theme-toggle.tsx
"use client";

import { useTheme } from "@wrksz/themes/client";

export function ThemeToggle() {
  const { resolvedTheme, setTheme } = useTheme();

  return (
    <button
      type="button"
      onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
    >
      Toggle theme
    </button>
  );
}

Import paths

@wrksz/themes/next in Next.js (recommended)
@wrksz/themes/client/use-themeDirect import
@wrksz/themes/client/use-theme-valueDirect import
@wrksz/themes/client/use-theme-effectDirect import
@wrksz/themes/client/themed-imageDirect import
@wrksz/themes/client/providerDirect import
@wrksz/themes/client/create-themesDirect import
@wrksz/themes/next/create-themesTyped with NextThemeProvider and ThemeScript
@wrksz/themes/next/extendedOpt-in Next.js with systemThemeMap and same-document sync
@wrksz/themes/client/extended-providerOpt-in with themeRoot, systemThemeMap, and same-document sync
@wrksz/themesClient-safe alias and for framework-neutral React usage
Note

The API is identical to next-themes. Migrating requires changing one import line: @wrksz/themes/next instead of next-themes. See Why not next-themes? for a full comparison.

Requirements

  • Next.js 16+
  • React 18+ (uses native useEffectEvent on React 19.2+)
  • TypeScript 5.9+ Breakingv2.0.0 (typescript peer is >=5.9 from 2.0.0; 4.x and 5.0–5.8 are unsupported on this line)

Next steps