@wrksz/themesv0.7.5
Examples

Server-provided theme

Eliminate SSR theme flash with cookie storage or a custom server-side source.

Edit on GitHub

Last updated on

Since v0.7.0

The simplest way to eliminate SSR theme flash. Use storage="cookie" with @wrksz/themes/next - the provider reads the cookie server-side automatically, no boilerplate required:

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

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="dark"
          storage="cookie"
          disableTransitionOnChange
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

Theme changes are written to document.cookie automatically. On subsequent requests the server reads the cookie via cookies() from next/headers and injects the correct class before the first paint.

Flash may still appear in Next.js development mode - this is expected. Next.js dev mode does not fully replicate SSR behavior. The flash disappears in production builds.

Cookie storage does not support cross-tab theme sync. If you need it, use the initialTheme approach below with localStorage.

Custom server-side source

Use initialTheme to initialize the theme from any server-side source (database, session, cookie) on every mount. The user can still call setTheme - use onThemeChange to persist the change back.

app/layout.tsx
import { ThemeProvider } from "@wrksz/themes/next";
import { getUserTheme, saveUserTheme } from "@/lib/user";

export default async function RootLayout({ children }) {
  const userTheme = await getUserTheme(); // "light" | "dark" | null

  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider
          initialTheme={userTheme ?? undefined}
          onThemeChange={saveUserTheme}
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

initialTheme also writes to storage on mount, so setTheme and cross-tab sync continue to work normally.

Priority

When both initialTheme and a stored value are present, initialTheme wins on every mount. This ensures the server-side value is always applied on page load.

On this page