@frank-auth/react
A comprehensive, highly configurable React authentication UI library for the Frank Auth multi-tenant SaaS platform.
🌟 Features
- 🎨 Highly Customizable: Server-driven configuration with complete theme control
- 🏢 Multi-Tenant: Built-in organization management and switching
- 👥 Three-Tier Users: Support for Internal, External, and End Users
- 🔐 Advanced Security: MFA, Passkeys, OAuth, Magic Links
- 🌍 Internationalization: 10+ languages with RTL support
- ♿ Accessibility: WCAG 2.1 AA compliant
- 📱 Responsive: Mobile-first design
- ⚡ Performance: Tree-shaking, lazy loading, optimized bundles
- 🎯 TypeScript: Full type safety and IntelliSense
- 🔧 Framework Agnostic: Works with Next.js, Remix, Vite, and more
🚀 Quick Start
Installation
npm install @frank-auth/react
# or
yarn add @frank-auth/react
# or
pnpm add @frank-auth/react
Basic Setup
import { FrankAuthProvider, SignIn, UserButton } from '@frank-auth/react';
function App() {
return (
<FrankAuthProvider publishableKey="pk_test_...">
<div className="app">
<nav>
<UserButton />
</nav>
<main>
<SignIn />
</main>
</div>
</FrankAuthProvider>
);
}
Advanced Setup with Configuration
import {
FrankAuthProvider,
SignIn,
UserButton,
createFrankAuthConfig
} from '@frank-auth/react';
const config = createFrankAuthConfig({
appearance: {
theme: 'auto',
colors: {
primary: '#6366f1',
background: '#ffffff',
foreground: '#0f172a',
},
elements: {
formButtonPrimary: 'bg-indigo-600 hover:bg-indigo-700',
card: 'shadow-xl border-2 border-indigo-100',
},
},
authentication: {
methods: ['email', 'oauth', 'passkey'],
mfa: {
enabled: true,
methods: ['totp', 'sms'],
},
oauth: {
providers: [
{ provider: 'google', enabled: true },
{ provider: 'github', enabled: true },
],
},
},
organization: {
enabled: true,
allowInvitations: true,
maxMembers: 50,
},
});
function App() {
return (
<FrankAuthProvider
publishableKey="pk_test_..."
config={config}
onAuthEvent={(event) => {
console.log('Auth event:', event);
}}
>
<SignIn mode="modal" />
<UserButton showProfile showOrganization />
</FrankAuthProvider>
);
}
🎯 Component Examples
Authentication Components
SignIn Component
import { SignIn } from '@frank-auth/react';
// Basic usage
<SignIn />
// Modal mode
<SignIn mode="modal" />
// Custom appearance
<SignIn
appearance={{
elements: {
formButtonPrimary: 'bg-blue-600 hover:bg-blue-700',
card: 'rounded-2xl shadow-2xl',
}
}}
/>
// With custom handlers
<SignIn
onSignInSuccess={(user) => {
console.log('Welcome back!', user);
// Custom redirect or action
}}
onSignInError={(error) => {
console.error('Sign in failed:', error);
// Custom error handling
}}
/>
// Limited authentication methods
<SignIn
allowedMethods={['email', 'oauth']}
redirectAfterSignIn="/dashboard"
/>
SignUp Component
import { SignUp } from '@frank-auth/react';
// Basic usage
<SignUp />
// With custom fields
<SignUp
customFields={[
{
id: 'company',
name: 'Company Name',
type: 'text',
required: true,
},
{
id: 'role',
name: 'Role',
type: 'select',
options: [
{ value: 'developer', label: 'Developer' },
{ value: 'designer', label: 'Designer' },
{ value: 'manager', label: 'Manager' },
],
},
]}
/>
UserButton Component
import { UserButton } from '@frank-auth/react';
// Basic usage
<UserButton />
// With custom actions
<UserButton
customActions={[
{
label: 'Settings',
action: () => navigate('/settings'),
icon: <SettingsIcon />,
},
{
label: 'Billing',
action: () => navigate('/billing'),
icon: <CreditCardIcon />,
},
]}
/>
// Show specific sections
<UserButton
showProfile={true}
showOrganization={true}
showSessions={true}
/>
Organization Components
Organization Switcher
import { OrganizationSwitcher } from '@frank-auth/react';
// Basic usage
<OrganizationSwitcher />
// With creation and switching
<OrganizationSwitcher
allowCreation={true}
allowSwitching={true}
showMembers={true}
/>
// Custom appearance
<OrganizationSwitcher
appearance={{
elements: {
organizationSwitcher: 'rounded-lg border-2',
}
}}
/>
Organization Profile
import { OrganizationProfile } from '@frank-auth/react';
// Full organization management
<OrganizationProfile
showMembers={true}
showSettings={true}
showBilling={true}
/>
🎨 Customization
Theme Configuration
const customTheme = {
name: 'custom-theme',
colors: {
primary: '#8b5cf6',
secondary: '#06b6d4',
background: '#fafafa',
foreground: '#18181b',
muted: '#f4f4f5',
accent: '#f59e0b',
destructive: '#ef4444',
border: '#e4e4e7',
input: '#ffffff',
ring: '#8b5cf6',
success: '#10b981',
warning: '#f59e0b',
error: '#ef4444',
},
typography: {
fontFamily: 'Inter, -apple-system, sans-serif',
fontSize: {
xs: '0.75rem',
sm: '0.875rem',
base: '1rem',
lg: '1.125rem',
xl: '1.25rem',
'2xl': '1.5rem',
'3xl': '1.875rem',
},
},
layout: {
borderRadius: '0.75rem',
spacing: {
xs: '0.25rem',
sm: '0.5rem',
md: '1rem',
lg: '1.5rem',
xl: '3rem',
},
},
};
<FrankAuthProvider
publishableKey="pk_test_..."
config={{
appearance: {
theme: customTheme,
}
}}
>
{/* Your app */}
</FrankAuthProvider>
Component Overrides
import { FrankAuthProvider } from '@frank-auth/react';
import { CustomSignIn, CustomUserButton } from './custom-components';
<FrankAuthProvider
publishableKey="pk_test_..."
config={{
components: {
SignIn: CustomSignIn,
UserButton: CustomUserButton,
}
}}
>
{/* Your app */}
</FrankAuthProvider>
CSS-in-JS Customization
const config = {
appearance: {
elements: {
// Form elements
formButtonPrimary: 'bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white font-semibold py-2 px-4 rounded-lg shadow-lg transform transition hover:scale-105',
formFieldInput: 'border-2 border-gray-200 focus:border-purple-500 rounded-lg px-3 py-2 transition-colors',
formFieldLabel: 'text-sm font-medium text-gray-700 mb-1',
// Cards
card: 'bg-white/80 backdrop-blur-sm border border-gray-200 rounded-2xl shadow-xl',
cardHeader: 'border-b border-gray-100 p-6',
cardBody: 'p-6',
// Modal
modalOverlay: 'fixed inset-0 bg-black/50 backdrop-blur-sm',
modalContent: 'bg-white rounded-2xl shadow-2xl max-w-md w-full mx-4',
// User interface
userButton: 'flex items-center space-x-2 px-3 py-2 rounded-lg hover:bg-gray-100 transition-colors',
organizationSwitcher: 'border border-gray-200 rounded-lg p-2 hover:bg-gray-50',
},
variables: {
'--frank-auth-primary': '#8b5cf6',
'--frank-auth-radius': '0.75rem',
'--frank-auth-shadow': '0 10px 25px -5px rgba(0, 0, 0, 0.1)',
},
},
};
🔧 Hooks Usage
useAuth Hook
import { useAuth } from '@frank-auth/react';
function MyComponent() {
const {
isLoaded,
isSignedIn,
user,
signOut,
organization,
switchOrganization
} = useAuth();
if (!isLoaded) {
return <div>Loading...</div>;
}
if (!isSignedIn) {
return <div>Please sign in</div>;
}
return (
<div>
<h1>Welcome, {user.firstName}!</h1>
<p>Organization: {organization?.name}</p>
<button onClick={() => signOut()}>
Sign Out
</button>
</div>
);
}
useOrganization Hook
import { useOrganization } from '@frank-auth/react';
function OrganizationDashboard() {
const {
organization,
members,
invitations,
isAdmin,
inviteMember,
removeMember
} = useOrganization();
const handleInvite = async (email: string, role: string) => {
try {
await inviteMember({ email, role });
toast.success('Invitation sent!');
} catch (error) {
toast.error('Failed to send invitation');
}
};
return (
<div>
<h1>{organization.name}</h1>
<p>{members.length} members</p>
{isAdmin && (
<button onClick={() => handleInvite('user@example.com', 'member')}>
Invite Member
</button>
)}
</div>
);
}
usePermissions Hook
import { usePermissions } from '@frank-auth/react';
function AdminPanel() {
const { hasPermission, hasRole, checkPermission } = usePermissions();
const canManageUsers = hasPermission('users:write');
const isAdmin = hasRole('admin');
const canDeletePosts = checkPermission('posts:delete', { resourceId: 'post-123' });
return (
<div>
{canManageUsers && (
<button>Manage Users</button>
)}
{isAdmin && (
<button>Admin Settings</button>
)}
{canDeletePosts && (
<button>Delete Post</button>
)}
</div>
);
}
🌐 Multi-Tenant Examples
Organization-Specific Branding
// This configuration is automatically loaded from your Frank Auth backend
// based on the current organization context
function App() {
return (
<FrankAuthProvider
publishableKey="pk_test_..."
organizationId="org_acme_corp"
onConfigLoad={(serverConfig) => {
// Server config includes organization-specific branding
console.log('Organization theme:', serverConfig.theme);
console.log('Organization logo:', serverConfig.branding.logo);
}}
>
<SignIn />
</FrankAuthProvider>
);
}
Dynamic User Type Handling
import { useAuth, configureForUserType } from '@frank-auth/react';
function AppWrapper() {
const { user } = useAuth();
// Configure based on user type
const config = configureForUserType(user?.type || 'end-users', {
appearance: {
theme: user?.type === 'internal' ? 'dark' : 'light',
},
});
return (
<FrankAuthProvider
publishableKey="pk_test_..."
config={config}
>
<App />
</FrankAuthProvider>
);
}
🔐 Advanced Security Features
Multi-Factor Authentication
import { useMfa } from '@frank-auth/react';
function SecuritySettings() {
const {
mfaEnabled,
availableMethods,
enableMfa,
disableMfa,
generateBackupCodes
} = useMfa();
const handleEnableMfa = async (method: 'totp' | 'sms') => {
try {
const qrCode = await enableMfa(method);
// Show QR code to user for TOTP setup
} catch (error) {
console.error('Failed to enable MFA:', error);
}
};
return (
<div>
<h2>Two-Factor Authentication</h2>
<p>Status: {mfaEnabled ? 'Enabled' : 'Disabled'}</p>
{!mfaEnabled && (
<div>
<button onClick={() => handleEnableMfa('totp')}>
Enable Authenticator App
</button>
<button onClick={() => handleEnableMfa('sms')}>
Enable SMS
</button>
</div>
)}
{mfaEnabled && (
<div>
<button onClick={generateBackupCodes}>
Generate Backup Codes
</button>
<button onClick={disableMfa}>
Disable MFA
</button>
</div>
)}
</div>
);
}
Passkey Integration
import { usePasskeys } from '@frank-auth/react';
function PasskeySettings() {
const {
passkeys,
registerPasskey,
removePasskey,
isSupported
} = usePasskeys();
if (!isSupported) {
return <div>Passkeys are not supported in this browser</div>;
}
const handleRegister = async () => {
try {
await registerPasskey({ name: 'My Device' });
toast.success('Passkey registered successfully!');
} catch (error) {
toast.error('Failed to register passkey');
}
};
return (
<div>
<h2>Passkeys</h2>
<button onClick={handleRegister}>
Add Passkey
</button>
<div className="mt-4">
{passkeys.map((passkey) => (
<div key={passkey.id} className="flex items-center justify-between p-3 border rounded">
<div>
<p className="font-medium">{passkey.name}</p>
<p className="text-sm text-gray-500">
Added {new Date(passkey.createdAt).toLocaleDateString()}
</p>
</div>
<button
onClick={() => removePasskey(passkey.id)}
className="text-red-600 hover:text-red-800"
>
Remove
</button>
</div>
))}
</div>
</div>
);
}
🚀 Framework Integration
Next.js Integration
// pages/_app.tsx
import { FrankAuthProvider } from '@frank-auth/react';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
<FrankAuthProvider publishableKey={process.env.NEXT_PUBLIC_FRANK_AUTH_PUBLISHABLE_KEY!}>
<Component {...pageProps} />
</FrankAuthProvider>
);
}
// pages/dashboard.tsx
import { RequireAuth } from '@frank-auth/react';
export default function Dashboard() {
return (
<RequireAuth>
<div>
<h1>Dashboard</h1>
{/* Protected content */}
</div>
</RequireAuth>
);
}
Remix Integration
// app/root.tsx
import { FrankAuthProvider } from '@frank-auth/react';
export default function App() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
</head>
<body>
<FrankAuthProvider publishableKey={ENV.FRANK_AUTH_PUBLISHABLE_KEY}>
<Outlet />
</FrankAuthProvider>
</body>
</html>
);
}
📱 Mobile and Responsive Design
All components are mobile-first and responsive by default:
// Components automatically adapt to screen size
<SignIn mode="card" /> // Becomes full-screen on mobile
// Custom responsive behavior
<UserButton
className="hidden md:block" // Hide on mobile
/>
<UserButton
className="md:hidden" // Show only on mobile
showProfile={false} // Simplified mobile view
/>
🌍 Internationalization
import { FrankAuthProvider } from '@frank-auth/react';
const config = {
localization: {
defaultLocale: 'en',
supportedLocales: ['en', 'es', 'fr', 'de', 'pt', 'it', 'ja', 'ko', 'zh'],
customTranslations: {
'es': {
'sign_in': 'Iniciar Sesión',
'sign_up': 'Registrarse',
'welcome_back': 'Bienvenido de vuelta',
},
},
},
};
<FrankAuthProvider
publishableKey="pk_test_..."
config={config}
>
{/* Components will use Spanish translations */}
</FrankAuthProvider>
🔧 Development and Debugging
Debug Mode
<FrankAuthProvider
publishableKey="pk_test_..."
config={{
advanced: {
debug: true, // Enable debug logging
telemetry: false, // Disable telemetry in development
}
}}
>
<App />
</FrankAuthProvider>
Dev Tools
import { AuthDevTools } from '@frank-auth/react';
function App() {
return (
<div>
{/* Your app */}
{process.env.NODE_ENV === 'development' && <AuthDevTools />}
</div>
);
}
📚 API Reference
Configuration Options
Option | Type | Description |
---|---|---|
publishableKey |
string |
Your Frank Auth publishable key |
organizationId |
string |
Current organization ID |
appearance |
AppearanceConfig |
Theme and styling configuration |
authentication |
AuthenticationConfig |
Authentication methods and settings |
organization |
OrganizationConfig |
Organization features and settings |
localization |
LocalizationConfig |
Language and locale settings |
components |
ComponentOverrides |
Custom component overrides |
advanced |
AdvancedConfig |
Advanced options and debugging |
Component Props
All components accept these common props:
Prop | Type | Description |
---|---|---|
className |
string |
Additional CSS classes |
appearance |
Partial<AppearanceConfig> |
Component-specific appearance |
onSuccess |
(result: any) => void |
Success callback |
onError |
(error: Error) => void |
Error callback |
🤝 Migration from ClerkJS
Frank Auth React is designed to be API-compatible with ClerkJS for easy migration:
// Before (ClerkJS)
import { ClerkProvider, SignIn, UserButton } from '@clerk/nextjs';
<ClerkProvider publishableKey="pk_test_...">
<SignIn />
<UserButton />
</ClerkProvider>
// After (Frank Auth)
import { FrankAuthProvider, SignIn, UserButton } from '@frank-auth/react';
<FrankAuthProvider publishableKey="pk_test_...">
<SignIn />
<UserButton />
</FrankAuthProvider>
📄 License
MIT License - see the LICENSE file for details.
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
📞 Support
- 📧 Email: support@frankauth.com
- 💬 Discord: Join our community
- 📖 Documentation: docs.frankauth.com
- 🐛 Issues: GitHub Issues
Made with ❤️ by the Frank Auth team