BETTER-AUTH. UI
Components

<AuthProvider>

Provides AuthConfig to descendant components.

Usage

import { Link, useNavigate, useParams } from "@tanstack/react-router"
import { ThemeProvider, useTheme } from "next-themes"
import type { ReactNode } from "react"
import { apiKeyPlugin } from "@/lib/auth/api-key-plugin"
import { deleteUserPlugin } from "@/lib/auth/delete-user-plugin"
import { emailOtpPlugin } from "@/lib/auth/email-otp-plugin"
import { magicLinkPlugin } from "@/lib/auth/magic-link-plugin"
import { multiSessionPlugin } from "@/lib/auth/multi-session-plugin"
import { organizationPlugin } from "@/lib/auth/organization-plugin"
import { passkeyPlugin } from "@/lib/auth/passkey-plugin"
import { themePlugin } from "@/lib/auth/theme-plugin"
import { twoFactorPlugin } from "@/lib/auth/two-factor-plugin"
import { usernamePlugin } from "@/lib/auth/username-plugin"
import { authClient } from "@/lib/auth-client"
import { AuthProvider } from "./auth/auth-provider"
import { Toaster } from "./ui/sonner"

export function Providers({ children }: { children: ReactNode }) {
  const navigate = useNavigate()
  const { slug } = useParams({ strict: false })

  return (
    <ThemeProvider
      attribute="class"
      defaultTheme="system"
      enableSystem
      disableTransitionOnChange
    >
      <AuthProvider
        authClient={authClient}
        redirectTo="/settings/account"
        socialProviders={["github"]}
        emailAndPassword={{ requireEmailVerification: false }}
        navigate={navigate}
        plugins={[
          usernamePlugin({
            usernamePrefix: "@",
            localization: { usernamePlaceholder: "username" }
          }),
          magicLinkPlugin(),
          emailOtpPlugin({
            emailVerification: true,
            passwordReset: true,
            changeEmail: true
          }),
          twoFactorPlugin(),
          passkeyPlugin(),
          apiKeyPlugin({
            organization: true,
            configurations: [
              { id: "default", label: "Personal", organization: false },
              { id: "organization", label: "Organization", organization: true }
            ]
          }),
          themePlugin({ useTheme }),
          multiSessionPlugin(),
          deleteUserPlugin(),
          organizationPlugin({
            slugPrefix: "@",
            slug: slug ?? null,
            teams: true
          })
        ]}
        Link={({ href, ...props }) => <Link to={href} {...props} />}
      >
        {children}

        <Toaster />
      </AuthProvider>
    </ThemeProvider>
  )
}

Localization

Install the locale package:

bun add @better-auth-ui/locales

Import one locale and pass it to AuthProvider:

components/providers.tsx
import { deDE } from "@better-auth-ui/locales/de-DE"

<AuthProvider authClient={authClient} locale={deDE} navigate={navigate}>
  {children}
</AuthProvider>

Locale bundles include the core messages and all built-in plugin messages.

Use localization for product-specific text. These values take priority over the selected locale:

<AuthProvider
  authClient={authClient}
  locale={deDE}
  localization={{ auth: { signIn: "Bei Acme anmelden" } }}
  navigate={navigate}
>
  {children}
</AuthProvider>

Match the browser language

In a client-only application, import the supported locales and match navigator.languages against that list:

import { matchAuthLocale } from "@better-auth-ui/locales"
import { deDE } from "@better-auth-ui/locales/de-DE"
import { enUS } from "@better-auth-ui/locales/en-US"

const locale = matchAuthLocale({
  requested: navigator.languages,
  supported: [enUS, deDE],
  fallback: enUS
})

For server rendering, resolve the same locale from a user preference or the Accept-Language header. Pass that locale during the first render to prevent a hydration mismatch.

Changing the locale prop updates mounted auth components. Email components do not read AuthProvider; pass their localization on the server.

Custom and Generic OAuth providers

Built-in providers use their Better Auth ID as a string. For a custom or Generic OAuth provider, pass its ID, visible label, and optional icon.

components/providers.tsx
import { Building2 } from "lucide-react"

<AuthProvider
  authClient={authClient}
  navigate={navigate}
  socialProviders={[
    "github",
    {
      id: "company-oauth",
      label: "Company SSO",
      icon: <Building2 />
    }
  ]}
>
  {children}
</AuthProvider>

The same metadata appears on sign-in, sign-up, and linked-account views. BAUI sends only id to Better Auth.

Better Auth 1.7 registers Generic OAuth providers as normal social providers. Configure the same ID on the server:

lib/auth.ts
import { betterAuth } from "better-auth"
import { genericOAuth } from "better-auth/plugins"

export const auth = betterAuth({
  plugins: [
    genericOAuth({
      config: [
        {
          providerId: "company-oauth",
          clientId: process.env.COMPANY_OAUTH_CLIENT_ID!,
          clientSecret: process.env.COMPANY_OAUTH_CLIENT_SECRET!,
          discoveryUrl:
            "https://id.example.com/.well-known/openid-configuration"
        }
      ]
    })
  ]
})

Register /api/auth/callback/company-oauth with the provider. See the Better Auth Generic OAuth guide for endpoint and profile options.

Set socialSignInMode="popup" to keep the current page open during social sign-in. Redirect mode remains the default.

Better Auth marks this API as experimental. Configure the server and client plugins before you enable it:

lib/auth.ts
import { betterAuth } from "better-auth"
import { bearer, oauthPopup } from "better-auth/plugins"

export const auth = betterAuth({
  plugins: [bearer(), oauthPopup()]
})
lib/auth-client.ts
import { createAuthClient } from "better-auth/react"
import { oauthPopupClient } from "better-auth/client/plugins"

export const authClient = createAuthClient({
  plugins: [oauthPopupClient()]
})

Then select popup mode on the provider:

<AuthProvider
  authClient={authClient}
  navigate={navigate}
  socialProviders={["github", "google"]}
  socialSignInMode="popup"
>
  {children}
</AuthProvider>

Popup mode uses the same provider buttons and redirect target. It returns control to the current page, refreshes the session, and then runs the configured navigation.

Props

Prop

Type

Last updated on

On this page