@wrksz/themesv2.1.1
API Reference

ClientThemeProvider

Theme provider for use inside Client Components.

ClientThemeProvider is the client-side counterpart of ThemeProvider. Use it when you need a nested provider inside a Client Component - renders an inline <script> which is not allowed in Client Components.

Two client entries exist. The default stays small. Shadow DOM, custom system maps, and same-document sync live on the opt-in extended import.

"use client";

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

export function AdminShell({ children }: { children: React.ReactNode }) {
  return (
    <ClientThemeProvider forcedTheme="dark">
      {children}
    </ClientThemeProvider>
  );
}

ThemeProvider vs ClientThemeProvider

ThemeProvider@wrksz/themes/nextRoot layout
Yes:
Inline script, no flash on load
Yes:
Server Components
No:
Client Components
Yes:
All default props
No:
themeRoot
ClientThemeProvider@wrksz/themes/clientNested providers
No:
Inline script
No:
Server Components
Yes:
Client Components
Yes:
All default props, nonce unused
No:
themeRoot
ClientThemeProvider@wrksz/themes/client/extended-providerShadow DOM, custom roots, same-document sync
No:
Inline script
No:
Server Components
Yes:
Client Components
Yes:
Default props, plus systemThemeMap and enableSameDocumentSync
Yes:
themeRoot, not on @wrksz/themes/next/extended

For your root layout, always use . Use ClientThemeProvider only for nested providers deeper in the tree.

Props

The default @wrksz/themes/client entry accepts the same props as ThemeProvider, with one exception: the nonce prop is not used since ClientThemeProvider renders no inline script.

It does not accept themeRoot, systemThemeMap, or enableSameDocumentSync. Those belong on @wrksz/themes/client/extended-provider. See Shadow DOM for themeRoot.

Examples

Scoped theming

Apply an independent theme to a specific section of your app by combining target and storage="none":

app/landing/layout.tsx
export default function LandingLayout({ children }) {
  return (
    <ThemeProvider forcedTheme="dark" target="#landing-root" storage="none">
      <div id="landing-root">{children}</div>
    </ThemeProvider>
  );
}
app/dashboard/layout.tsx
export default function DashboardLayout({ children }) {
  return (
    <ThemeProvider forcedTheme="light" target="#dashboard-root" storage="none">
      <div id="dashboard-root">{children}</div>
    </ThemeProvider>
  );
}
#landing-root { --bg: #0a0a0a; --fg: #fafafa; }
#dashboard-root { --bg: #ffffff; --fg: #0a0a0a; }

For a client-owned Element or ShadowRoot (no selector, no bootstrap), use themeRoot on the extended client provider.

Nested provider in a Client Component

"use client";

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

export function Sidebar({ children }: { children: React.ReactNode }) {
  return (
    <ClientThemeProvider storageKey="sidebar-theme">
      {children}
    </ClientThemeProvider>
  );
}

Each ClientThemeProvider maintains its own independent theme state - nested providers do not affect each other.

Shadow DOM

Import the extended client provider and pass the ShadowRoot from attachShadow. There is no anti-flash script: the bootstrap cannot see a client-owned shadow tree.

import { ClientThemeProvider } from "@wrksz/themes/client/extended-provider";

<ClientThemeProvider themeRoot={shadowRoot} storage="none" defaultTheme="dark">
  {children}
</ClientThemeProvider>

Full walkthrough, :host(.dark) CSS, and why target is ignored when themeRoot is set: Shadow DOM.