BETTER-AUTH. UI
Plugins

Theme

Add theme selection with system, light, and dark modes to your authentication flow.

The theme plugin adds theme selection to your authentication UI. Users can switch between system, light, and dark themes from the user button dropdown and account settings.

Setup

Install the UI plugin

Run the shadcn CLI to install the theme plugin components:

npx shadcn@latest add https://better-auth-ui.com/r/theme.json

Register the UI plugin

The plugin is theme-library agnostic. The recommended setup is to pass your theme library's hook (e.g. useTheme from next-themes) so the plugin's slot components read live theme state inside your <ThemeProvider> — no inner wrapper required.

components/providers.tsx
import { ThemeProvider, useTheme } from "next-themes"
import type { ReactNode } from "react"

import { AuthProvider } from "@/components/auth/auth-provider"
import { authClient } from "@/lib/auth-client"
import { themePlugin } from "@/lib/auth/theme-plugin"

export function Providers({ children }: { children: ReactNode }) {
  return (
    <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
      <AuthProvider
        authClient={authClient}
        plugins={[
          themePlugin({ useTheme }) 
        ]}
      >
        {children}
      </AuthProvider>
    </ThemeProvider>
  )
}

The plugin invokes useTheme() inside its slot components on every render, so the call always happens within your <ThemeProvider> even when both providers live in the same component.

Or pass static theme state

If you don't have a hook (e.g. you're managing the theme with useState or a custom controller), pass both theme and setTheme from a stateful source. The plugin re-runs on every render of the parent, so slot components stay in sync as long as your state owner re-renders on change.

components/providers.tsx
const [theme, setTheme] = useState("system") 

<AuthProvider
  authClient={authClient}
  plugins={[
    themePlugin({ 
      theme, 
      setTheme, 
      themes: ["system", "light", "dark"] 
    }) 
  ]}
>
  {children}
</AuthProvider>

The two forms are mutually exclusive — you pass either useTheme or the theme/setTheme pair.

Components

<UserButton />

<Appearance />

Appearance

The <Appearance /> card is automatically rendered in <AccountSettings /> when the plugin is registered.

Usage

import { Appearance } from "@/components/auth/theme/appearance"

<Appearance />

Props

Prop

Type

Options

Prop

Type

Localization

Prop

Type

Last updated on

On this page