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.jsonRegister 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.
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.
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