/**
* React Client Components with better-auth
*
* This example demonstrates:
* - useSession hook
* - Sign in/up forms
* - Social sign-in buttons
* - Protected route component
* - User profile component
* - Organization switcher
*/
'use client'
import { createAuthClient, useSession } from 'better-auth/client'
import { useState, useEffect } from 'react'
// Initialize auth client
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'
})
// ============================================================================
// Login Form Component
// ============================================================================
export function LoginForm() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const handleEmailSignIn = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
setLoading(true)
try {
const { data, error } = await authClient.signIn.email({
email,
password
})
if (error) {
setError(error.message)
return
}
// Redirect on success
window.location.href = '/dashboard'
} catch (err) {
setError('An error occurred. Please try again.')
} finally {
setLoading(false)
}
}
const handleGoogleSignIn = async () => {
setLoading(true)
await authClient.signIn.social({
provider: 'google',
callbackURL: '/dashboard'
})
}
const handleGitHubSignIn = async () => {
setLoading(true)
await authClient.signIn.social({
provider: 'github',
callbackURL: '/dashboard'
})
}
return (
Sign In
{error && (
{error}
)}
Don't have an account?{' '}
Sign up
)
}
// ============================================================================
// Sign Up Form Component
// ============================================================================
export function SignUpForm() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [name, setName] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const [success, setSuccess] = useState(false)
const handleSignUp = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
setLoading(true)
try {
const { data, error } = await authClient.signUp.email({
email,
password,
name
})
if (error) {
setError(error.message)
return
}
setSuccess(true)
} catch (err) {
setError('An error occurred. Please try again.')
} finally {
setLoading(false)
}
}
if (success) {
return (
Check your email
We've sent a verification link to {email}.
Click the link to verify your account.
)
}
return (
Sign Up
{error && (
{error}
)}
Already have an account?{' '}
Sign in
)
}
// ============================================================================
// User Profile Component
// ============================================================================
export function UserProfile() {
const { data: session, isPending } = useSession()
if (isPending) {
return Loading...
}
if (!session) {
return (
)
}
const handleSignOut = async () => {
await authClient.signOut()
window.location.href = '/login'
}
return (
{session.user.image && (

)}
{session.user.name}
{session.user.email}
)
}
// ============================================================================
// Protected Route Component
// ============================================================================
export function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { data: session, isPending } = useSession()
if (isPending) {
return (
)
}
if (!session) {
// Redirect to login
if (typeof window !== 'undefined') {
window.location.href = '/login'
}
return null
}
return <>{children}>
}
// ============================================================================
// Organization Switcher Component (if using organizations plugin)
// ============================================================================
export function OrganizationSwitcher() {
const { data: session } = useSession()
const [organizations, setOrganizations] = useState([])
const [loading, setLoading] = useState(true)
// Fetch user's organizations
useEffect(() => {
async function fetchOrgs() {
const orgs = await authClient.organization.listUserOrganizations()
setOrganizations(orgs)
setLoading(false)
}
fetchOrgs()
}, [])
const switchOrganization = async (orgId: string) => {
await authClient.organization.setActiveOrganization({ organizationId: orgId })
window.location.reload()
}
if (loading) return Loading organizations...
return (
)
}
// ============================================================================
// 2FA Setup Component (if using twoFactor plugin)
// ============================================================================
export function TwoFactorSetup() {
const [qrCode, setQrCode] = useState('')
const [verifyCode, setVerifyCode] = useState('')
const [enabled, setEnabled] = useState(false)
const enable2FA = async () => {
const { data } = await authClient.twoFactor.enable({ method: 'totp' })
setQrCode(data.qrCode)
}
const verify2FA = async (e: React.FormEvent) => {
e.preventDefault()
const { error } = await authClient.twoFactor.verify({ code: verifyCode })
if (!error) {
setEnabled(true)
}
}
if (enabled) {
return 2FA is enabled!
}
if (qrCode) {
return (
Scan QR Code
)
}
return (
)
}