next.jsnext.js13clerk

Handle theme in nextjs with clerk


I have a nextjs 13 application and I'm using Clerk for authentication. I'm trying to match the theme of the app to the ClerkProvider component. I used the useTheme hook from next-themes to get the resolvedTheme and assign the theme accordingly, but that required converting the layout.tsx into a client component which misses up with the metadata. I'm wondering if there is a better way to handle this.

Here is my code:

import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { ClerkProvider } from "@clerk/nextjs";
import { ThemeProvider } from "@/components/theme-provider";
import { useTheme } from "next-themes";
import { dark } from "@clerk/themes";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const { resolvedTheme } = useTheme();
  return (
    <ClerkProvider
      appearance={{
        baseTheme: resolvedTheme === "dark" ? dark : undefined,
      }}
    >
      <html lang="en" suppressHydrationWarning>
        <body className={inter.className}>
          <ThemeProvider
            attribute="class"
            defaultTheme="system"
            enableSystem
            disableTransitionOnChange
          >
            {children}
          </ThemeProvider>
        </body>
      </html>
    </ClerkProvider>
  );
}

Solution

  • Rather than setting it from Clerk → ThemeProvider, I suggest to do it the other way round.

    As mentioned in the answer by Ahmad Mughal, you won't be able to set the appearance in the ClerkProvider globally, since it's expected to run only on the server.

    You can however set the appearance on the level of Clerk components where you can use useTheme() (if you declare the page a client component with 'use client').

    A page like this will render the appearance of the Clerk SignIn component according to the current theme from the ThemeProvider:

    "use client";
    
    import { SignIn } from "@clerk/nextjs";
    import { dark } from "@clerk/themes";
    import { useTheme } from "next-themes";
    
    const PageWithSignIn = () => {
      const { currentTheme } = useTheme();
    
      return (
        <SignIn
          appearance={{
            baseTheme: currentTheme === "dark" ? dark : undefined,
          }}
        />
      );
    };
    
    export default Page;