npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

topiray-auth-react

v1.2.0

Published

A customizable React authentication component library

Readme

Topiray Auth React Component Library

NPM Version NPM Downloads NPM License

A comprehensive, themeable React component library for authentication flows. Built with TypeScript and CSS Modules, designed for customization, and tested against WCAG 2.2 Level AA — see Accessibility.

Features

  • 🎨 Fully Themeable - Customize colors, fonts, spacing, and more
  • 🔧 CSS Modules - Strongly-typed, scoped styling
  • 📱 Responsive Design - Mobile-first approach with breakpoint support
  • Accessible - ARIA labels, keyboard navigation, focus management
  • 🔐 Complete Auth Flow - Sign in/up, 2FA, email verification, password reset
  • TypeScript - Full type safety and IntelliSense support
  • 🎯 Tree Shakeable - Import only what you need
  • 🌐 i18n Support - Built-in internationalization with customizable translations

Installation

npm install topiray-auth-react
# or
yarn add topiray-auth-react

Peer Dependencies

npm install react react-dom lucide-react @mui/material

CSS Import

Important: You must import the CSS file for proper styling:

// Import the CSS file in your main App component or index file
import 'topiray-auth-react/dist/topiray-auth-react.css'

Or if you're using a bundler that supports CSS imports:

/* In your main CSS file */
@import 'topiray-auth-react/dist/topiray-auth-react.css';

Quick Start

import React from 'react'
import { 
  ThemeProvider, 
  defaultTheme, 
  TwoPanelLayout, 
  AuthCard, 
  SignInForm 
} from 'topiray-auth-react'

// Import the CSS file
import 'topiray-auth-react/dist/topiray-auth-react.css'

function App() {
  const handleSignIn = (email: string, password: string) => {
    console.log('Sign in:', { email, password })
  }

  return (
    <ThemeProvider theme={defaultTheme}>
      <TwoPanelLayout
        rightContent={
          <AuthCard>
            <SignInForm
              onSubmit={handleSignIn}
              onForgotPassword={() => console.log('Forgot password')}
              onSignUp={() => console.log('Sign up')}
              onSocialLogin={(provider) => console.log('Social:', provider)}
              authProviders={['google', 'apple', 'facebook']}
            />
          </AuthCard>
        }
      />
    </ThemeProvider>
  )
}

Components

Auth Components

AuthCard

Container wrapper for auth forms with consistent styling.

import { AuthCard } from 'topiray-auth-react'

<AuthCard>
  {/* Your auth form content */}
</AuthCard>

SignInForm

Complete sign-in form with email/password and customizable social login options.

import { SignInForm } from 'topiray-auth-react'

<SignInForm
  onSubmit={(email, password) => handleSignIn(email, password)}
  onForgotPassword={() => navigate('/forgot-password')}
  onSignUp={() => navigate('/sign-up')}
  onSocialLogin={(provider) => handleSocialLogin(provider)}
  authProviders={['google', 'apple', 'facebook']}
  isLoading={isSigningIn}
  logoSrc="/your-logo.png"
/>

SignUpForm

Account creation form with business/individual selection and customizable social login.

import { SignUpForm } from 'topiray-auth-react'

<SignUpForm
  onSubmit={(email, password, accountType) => handleSignUp(email, password, accountType)}
  onSignIn={() => navigate('/sign-in')}
  onSocialLogin={(provider) => handleSocialLogin(provider)}
  authProviders={['google', 'apple', 'facebook']}
  isLoading={isSigningUp}
  logoSrc="/your-logo.png"
/>

ForgottenPasswordForm

Password reset request form.

import { ForgottenPasswordForm } from 'topiray-auth-react'

<ForgottenPasswordForm
  onSubmit={(email) => handlePasswordReset(email)}
  isLoading={isLoading}
/>

VerifyEmailForm

Email verification flow with resend functionality.

import { VerifyEmailForm } from 'topiray-auth-react'

<VerifyEmailForm
  onSubmit={() => navigate('/sign-in')}
  onResendEmail={() => handleResendEmail()}
  email="[email protected]"
  isLoading={isLoading}
/>

Two-Factor Authentication

TwoFactorSetupForm

QR code setup for authenticator apps.

import { TwoFactorSetupForm } from 'topiray-auth-react'

<TwoFactorSetupForm
  onNext={() => navigate('/2fa/verify')}
  onCancel={() => navigate('/dashboard')}
  qrCodeUri="otpauth://totp/App:[email protected]?secret=SECRET&issuer=App"
  sharedKey="JBSWY3DPEHPK3PXP"
  isLoading={isLoading}
/>

TwoFactorSetupEnterVerificationForm

6-digit code entry with auto-focus and paste support.

import { TwoFactorSetupEnterVerificationForm } from 'topiray-auth-react'

<TwoFactorSetupEnterVerificationForm
  onVerify={(code) => handleVerification(code)}
  isLoading={isVerifying}
  error={error}
  backRoute="/2fa/setup"
/>

TwoFactorSetupCompleteForm

Backup codes display with copy/download functionality.

import { TwoFactorSetupCompleteForm } from 'topiray-auth-react'

<TwoFactorSetupCompleteForm
  onDone={() => navigate('/dashboard')}
  backupCodes={recoveryCodes}
  isLoading={isLoading}
/>

Layout Components

TwoPanelLayout

Responsive layout with left branding panel and right content area.

import { TwoPanelLayout } from 'topiray-auth-react'

<TwoPanelLayout
  leftContent={<YourBrandingContent />}
  rightContent={<YourAuthForm />}
  leftBackgroundImage="/background.jpg"
  logoSrc="/logo.png"
/>

NavLinksLayout

Flexible content layout for pages with navigation.

import { NavLinksLayout } from 'topiray-auth-react'

<NavLinksLayout
  middle={<MainContent />}
  right={<SidebarContent />}
/>

Common Components

Button

Versatile button with loading states and variants.

import { Button } from 'topiray-auth-react'

<Button variant="primary" isLoading={isLoading} fullWidth>
  Sign In
</Button>

<Button variant="secondary" icon="google">
  Sign in with Google
</Button>

AlertMessage

Dismissible alert messages for different states.

import { AlertMessage } from 'topiray-auth-react'

<AlertMessage 
  type="error" 
  message="Invalid credentials" 
  dismissible
  onDismiss={() => setError(null)}
/>

SocialLoginButtons

Pre-configured social login buttons.

import { SocialLoginButtons } from 'topiray-auth-react'

<SocialLoginButtons
  onSocialLogin={(provider) => handleSocialLogin(provider)}
  providers={['google', 'apple', 'facebook']}
  orientation="vertical"
  isLoading={isLoading}
/>

Customizing Social Login Providers

You can customize which social login providers are displayed using the authProviders prop on both SignInForm and SignUpForm:

// Show only Google and Apple
<SignInForm
  authProviders={['google', 'apple']}
  onSocialLogin={(provider) => handleSocialLogin(provider)}
  // ... other props
/>

// Show all available providers (default)
<SignUpForm
  authProviders={['apple', 'google', 'facebook']}
  onSocialLogin={(provider) => handleSocialLogin(provider)}
  // ... other props
/>

// Show only Google
<SignInForm
  authProviders={['google']}
  onSocialLogin={(provider) => handleSocialLogin(provider)}
  // ... other props
/>

// Hide social login entirely by setting empty array or omitting the prop
<SignInForm
  authProviders={[]}
  // ... other props (onSocialLogin not needed if no providers)
/>

Available providers:

  • 'apple' - Apple Sign In
  • 'google' - Google Sign In
  • 'facebook' - Facebook Login

Default behavior: If authProviders is not specified, it defaults to ['apple', 'google', 'facebook'].

Real-World Usage Examples

Complete Sign-In Page

import React, { useState, useEffect } from 'react'
import { useNavigate, useLocation } from 'react-router-dom'
import {
  ThemeProvider,
  defaultTheme,
  TwoPanelLayout,
  AuthCard,
  SignInForm,
  AlertMessage
} from 'topiray-auth-react'

// Import the CSS file
import 'topiray-auth-react/dist/topiray-auth-react.css'

function SignInPage() {
  const navigate = useNavigate()
  const location = useLocation()
  const [error, setError] = useState<string | null>(null)
  const [isLoading, setIsLoading] = useState(false)

  // Get success message from registration
  const successMessage = location.state?.message

  const handleSignIn = async (email: string, password: string) => {
    try {
      setError(null)
      setIsLoading(true)
      
      const result = await signInAPI(email, password)
      
      if (result.succeeded) {
        navigate('/dashboard')
      } else if (result.requiresTwoFactor) {
        navigate('/2fa/signin', { state: { userId: result.userId } })
      } else if (result.isNotAllowed) {
        navigate(`/verify-email?email=${encodeURIComponent(email)}`)
      } else {
        setError('Invalid email or password.')
      }
    } catch (error) {
      setError('An error occurred during sign in.')
    } finally {
      setIsLoading(false)
    }
  }

  const handleSocialLogin = async (provider: 'google' | 'apple' | 'facebook') => {
    try {
      setError(null)
      setIsLoading(true)
      
      // Initiate social login flow
      await socialSignInAPI(provider)
    } catch (error) {
      setError(`Failed to sign in with ${provider}`)
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <ThemeProvider theme={defaultTheme}>
      <TwoPanelLayout
        rightContent={
          <AuthCard>
            {successMessage && (
              <AlertMessage message={successMessage} type="success" />
            )}
            {error && (
              <AlertMessage message={error} type="error" />
            )}
            <SignInForm
              onSubmit={handleSignIn}
              onForgotPassword={() => navigate('/forgot-password')}
              onSignUp={() => navigate('/sign-up')}
              onSocialLogin={handleSocialLogin}
              authProviders={['google', 'apple']} // Only show Google and Apple
              isLoading={isLoading}
            />
          </AuthCard>
        }
      />
    </ThemeProvider>
  )
}

Two-Factor Setup with QR Code

import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import QRCode from 'qrcode'
import {
  TwoPanelLayout,
  AuthCard,
  TwoFactorSetupForm,
  AlertMessage
} from 'topiray-auth-react'

function TwoFactorSetupPage() {
  const navigate = useNavigate()
  const [qrCodeDataUrl, setQrCodeDataUrl] = useState<string | null>(null)
  const [sharedKey, setSharedKey] = useState<string | null>(null)
  const [error, setError] = useState<string | null>(null)
  const [isLoading, setIsLoading] = useState(false)

  useEffect(() => {
    generateQRCode()
  }, [])

  const generateQRCode = async () => {
    try {
      setIsLoading(true)
      const result = await createAuthenticatorKeyAPI()
      
      if (result.authenticatorUri && result.sharedKey) {
        const qrDataUrl = await QRCode.toDataURL(result.authenticatorUri, {
          width: 200,
          margin: 2
        })
        
        setQrCodeDataUrl(qrDataUrl)
        setSharedKey(result.sharedKey)
        
        // Render QR code
        setTimeout(() => {
          const container = document.getElementById('qr-code-container')
          if (container && qrDataUrl) {
            container.innerHTML = `<img src="${qrDataUrl}" alt="QR Code" />`
          }
        }, 100)
      } else {
        setError('Failed to generate QR code.')
      }
    } catch (error) {
      setError('Failed to generate QR code.')
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <TwoPanelLayout
      rightContent={
        <AuthCard>
          {error && <AlertMessage message={error} type="error" />}
          <TwoFactorSetupForm
            onNext={() => navigate('/2fa/verify')}
            onCancel={() => navigate('/dashboard')}
            qrCodeUri={qrCodeDataUrl}
            sharedKey={sharedKey}
            isLoading={isLoading}
          />
        </AuthCard>
      }
    />
  )
}

Theming

Using Built-in Themes

import { ThemeProvider, defaultTheme, darkTheme } from 'topiray-auth-react'

// Light theme
<ThemeProvider theme={defaultTheme}>
  <App />
</ThemeProvider>

// Dark theme
<ThemeProvider theme={darkTheme}>
  <App />
</ThemeProvider>

Creating Custom Themes

import { createCustomTheme } from 'topiray-auth-react'

const corporateTheme = createCustomTheme({
  colors: {
    primary: '#0066cc',
    secondary: '#6b7280',
    tertiary: "#f3f4f6",
    background: '#ffffff',
    surface: '#f9fafb',
    text: '#111827',
    textSecondary: '#6b7280',
    border: '#d1d5db',
    success: '#10b981',
    warning: '#f59e0b',
    error: '#ef4444',
    info: '#3b82f6',
    hover: '#f3f4f6',
    active: '#e5e7eb',
    disabled: '#9ca3af',
    inputBackground: '#ffffff',
    inputBorder: '#d1d5db',
    inputText: '#111827',
    inputPlaceholder: '#9ca3af',
    buttonPrimary: '#0066cc',
    buttonPrimaryText: '#ffffff',
    buttonSecondary: '#6b7280',
    buttonSecondaryText: '#ffffff',
    buttonSocial: '#374151',
    buttonSocialText: '#ffffff'
  },
  brand: {
    logo: '/corporate-logo.png',
    logoAlt: 'Corporate Logo'
  },
  customization: {
    showFormHeader: true,
    showSocialLogin: true,
    showBackArrow: true,
    showLogo: true,
    roundedCorners: true,
    animations: true
  }
})

<ThemeProvider theme={corporateTheme}>
  <App />
</ThemeProvider>

Advanced Theme Customization

const advancedTheme = createCustomTheme({
  colors: {
    // Custom color palette
    primary: '#7c3aed',
    secondary: '#64748b',
    // ... other colors
  },
  components: {
    spacing: {
      xs: '0.25rem',
      sm: '0.5rem',
      md: '1rem',
      lg: '1.5rem',
      xl: '2rem'
    },
    borderRadius: {
      sm: '0.25rem',
      md: '0.5rem',
      lg: '0.75rem',
      xl: '1rem'
    },
    typography: {
      fontFamily: '"Inter", system-ui, sans-serif',
      fontSize: {
        xs: '0.75rem',
        sm: '0.875rem',
        md: '1rem',
        lg: '1.125rem',
        xl: '1.25rem',
        xxl: '1.5rem'
      }
    }
  },
  customization: {
    backgroundImage: '/custom-background.jpg',
    leftPanelContent: <CustomBrandingComponent />
  }
})

CSS Custom Properties

The library uses CSS custom properties for theming. Make sure to import the CSS file first, then you can override these properties:

// First, import the CSS file
import 'topiray-auth-react/dist/topiray-auth-react.css'
/* Then override custom properties in your CSS */
:root {
  --topiray-color-primary: #your-brand-color;
  --topiray-color-surface: #your-surface-color;
  --topiray-font-family: 'Your Font', sans-serif;
  --topiray-radius-md: 12px;
  --topiray-spacing-lg: 24px;
}

Properties with a contrast threshold

These carry an accessibility requirement, so if you override one, measure it. Each is also settable through the theme object (focusRing, link, errorSurface, errorText, and so on for success, warning and info).

| Property | Requirement | |---|---| | --topiray-color-focus-ring | 3:1 against the page background — it is the only thing showing where the keyboard is (1.4.11) | | --topiray-color-link | 4.5:1 against the background behind it (1.4.3) | | --topiray-color-input-border | 3:1 against the surface behind the field, or the field's own fill must supply that contrast (1.4.11) | | --topiray-button-primary and --topiray-button-primary-text | 4.5:1 against each other (1.4.3) | | --topiray-color-error-surface / -error-text | 4.5:1 against each other. Likewise success, warning, info |

Two of these are worth spelling out, because both were wrong in this library until they were measured:

  • A tint of an accent colour is not a background you can put that accent colour on. The alerts used a 10% alpha tint of each state colour with the state colour as text, which measured between 1.91:1 and 3.16:1. Alpha also meant the true ratio depended on whatever was painted behind the alert, so it could not be relied on at all.
  • A focus ring flush against a control has no guaranteed contrast with it. The ring is drawn with outline-offset, so it sits on the page background — a blue ring directly on a blue button is a 1:1 ratio however carefully the colour was picked.

Integration Patterns

With React Router

import { BrowserRouter, Routes, Route } from 'react-router-dom'

// Import the CSS file once at the app level
import 'topiray-auth-react/dist/topiray-auth-react.css'

function App() {
  return (
    <ThemeProvider theme={defaultTheme}>
      <BrowserRouter>
        <Routes>
          <Route path="/signin" element={<SignInPage />} />
          <Route path="/signup" element={<SignUpPage />} />
          <Route path="/forgot-password" element={<ForgotPasswordPage />} />
          <Route path="/verify-email" element={<VerifyEmailPage />} />
          <Route path="/2fa/setup" element={<TwoFactorSetupPage />} />
          <Route path="/2fa/verify" element={<TwoFactorVerifyPage />} />
          <Route path="/2fa/complete" element={<TwoFactorCompletePage />} />
        </Routes>
      </BrowserRouter>
    </ThemeProvider>
  )
}

With State Management

// Using React Context
const AuthContext = createContext()

function AuthProvider({ children }) {
  const [user, setUser] = useState(null)
  const [isLoading, setIsLoading] = useState(false)

  const login = async (email, password) => {
    setIsLoading(true)
    try {
      const result = await signInAPI(email, password)
      setUser(result.user)
      return result
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <AuthContext.Provider value={{ user, isLoading, login }}>
      {children}
    </AuthContext.Provider>
  )
}

With Form Validation

import { z } from 'zod'

const signInSchema = z.object({
  email: z.string().email('Invalid email address'),
  password: z.string().min(8, 'Password must be at least 8 characters')
})

function SignInPage() {
  const [errors, setErrors] = useState({})

  const handleSignIn = (email, password) => {
    try {
      signInSchema.parse({ email, password })
      setErrors({})
      // Proceed with sign in
    } catch (error) {
      setErrors(error.flatten().fieldErrors)
    }
  }

  return (
    <SignInForm
      onSubmit={handleSignIn}
      authProviders={['google', 'apple']}
      // Pass validation errors to form
    />
  )
}

TypeScript Support

Full TypeScript support with comprehensive type definitions:

import type { 
  ThemeConfig, 
  SignInFormProps, 
  AuthCardProps 
} from 'topiray-auth-react'

// Custom theme with full type safety
const myTheme: ThemeConfig = {
  colors: {
    primary: '#0066cc',
    // ... TypeScript will validate all required properties
  },
  // ... rest of theme configuration
}

// Component props are fully typed
const handleSignIn: SignInFormProps['onSubmit'] = (email, password) => {
  // email and password are properly typed as strings
}

// Social login providers are strictly typed
const handleSocialLogin = (provider: 'google' | 'apple' | 'facebook') => {
  // provider parameter is type-safe
}

Accessibility

The library targets WCAG 2.2 Level AA, which is the standard UK public sector bodies are held to by the Public Sector Bodies (Websites and Mobile Applications) (No. 2) Accessibility Regulations 2018.

What is checked, and how

Conformance is measured rather than asserted. npm test runs a suite against a real browser — every component, in both the light and dark themes, at desktop width and at 320 CSS px:

| Check | Covers | |---|---| | axe-core via @axe-core/playwright, tags wcag2a, wcag2aa, wcag21a, wcag21aa, wcag22aa | 1.1.1, 1.3.1, 1.4.3, 3.3.2, 4.1.2 and everything else a rule engine can decide | | Focus visibility | 2.4.7 — each control is photographed focused and unfocused and the two must differ | | Target size | 2.5.8 — 24x24 CSS px, implementing the criterion's inline and spacing exceptions | | Non-text contrast | 1.4.11 — control boundaries at 3:1, which axe has no rule for | | Reflow | 1.4.10 — no horizontal scrolling at 320 CSS px | | Semantics | Labels that survive typing, autocomplete tokens, live regions, heading levels, radio group behaviour |

The last row exists because a rule engine cannot see the failures that matter most here. axe raised no objection to the credential fields when they were labelled by placeholder alone: a placeholder does contribute an accessible name, so the rule passes — and the name still disappears the moment somebody types. A clean axe run is evidence, not a conformance claim.

Areas that remain unverified are listed honestly: no screen reader pass (NVDA, JAWS, VoiceOver) has been carried out, and no testing has been done with disabled users. Automated testing finds somewhere between a third and a half of real accessibility problems and none of the ones requiring judgement.

What the components do for you

  • Visible, persistent labels on every credential field, associated by for/id. Not placeholders (3.3.2, 2.5.3).
  • Autofill tokensemail, current-password, new-password, one-time-code — so a password manager can recognise the field (1.3.5, 3.3.8).
  • Headings for form titles, at a level you choose with headingLevel, defaulting to h1 (1.3.1, 2.4.6).
  • A visible focus indicator on every control: a 3px outline offset from the control so it lands on the page background rather than on the control's own fill (2.4.7, 1.4.11).
  • Colours measured against their own surfaces, including the four alert styles and the primary button (1.4.3).
  • Live regions for status messages, so "Copied!" reaches somebody who is not looking at the button (4.1.3).
  • A submit button that keeps focus while it is working — marked aria-disabled and aria-busy rather than natively disabled, which would drop the keyboard user back to the top of the document at the moment they pressed it.
  • Reduced motion honoured — every transition and hover translation is switched off under prefers-reduced-motion: reduce.
  • Translated accessible names. Every name assistive technology reads comes from the dictionary, so a Welsh page does not announce its buttons in English.

Choosing a heading level

Each form renders its title as an h1 by default, which is right when the form is the page. If your application already renders an h1 around it, say so:

<SignInForm headingLevel="h2" onSubmit={handleSignIn} />

Running the suite

npm test                 # the whole suite, headless
npm run test:a11y:ui     # the Playwright UI, for working through a failure
npm run a11y             # the harness on its own, at http://127.0.0.1:5177/a11y.html

A failure stops a release: the accessibility job in the publish workflow is a blocking gate, not an advisory one.

Internationalization (i18n)

All auth components support internationalization out of the box. English is the default language. You can provide custom translations for any language by wrapping your app with the I18nProvider.

Default Behavior (English)

Without any i18n configuration, all components render in English. No setup is required.

Adding a New Language

  1. Create a translation JSON file following the same structure as the built-in English translations. All keys live under the topiray.auth namespace:
// fr.json
{
  "topiray": {
    "auth": {
      "signIn": {
        "title": "Se connecter",
        "emailPlaceholder": "E-mail",
        "passwordPlaceholder": "Mot de passe",
        "submitButton": "Continuer",
        "submittingButton": "Connexion en cours...",
        "dividerText": "ou se connecter avec",
        "forgotPassword": "Mot de passe oublié ?",
        "noAccount": "Vous n'avez pas de compte ?",
        "signUpLink": "S'inscrire"
      },
      "signUp": {
        "title": "Créez votre compte",
        "emailPlaceholder": "E-mail",
        "passwordPlaceholder": "Mot de passe",
        "submitButton": "Créer un compte",
        "submittingButton": "Création du compte...",
        "dividerText": "ou s'inscrire avec",
        "hasAccount": "Vous avez déjà un compte ?",
        "signInLink": "Se connecter"
      },
      "forgottenPassword": {
        "title": "Mot de passe oublié ?",
        "description": "Entrez l'adresse e-mail associée à votre compte et nous vous enverrons un lien unique pour réinitialiser votre mot de passe.",
        "emailPlaceholder": "E-mail",
        "submitButton": "Réinitialiser le mot de passe",
        "submittingButton": "Envoi en cours..."
      },
      "resetPassword": {
        "title": "Réinitialiser votre mot de passe",
        "description": "Entrez votre nouveau mot de passe ci-dessous. Assurez-vous qu'il est sécurisé et comporte au moins 8 caractères.",
        "newPasswordPlaceholder": "Nouveau mot de passe",
        "confirmPasswordPlaceholder": "Confirmer le mot de passe",
        "submitButton": "Réinitialiser le mot de passe",
        "submittingButton": "Réinitialisation...",
        "errors": {
          "passwordRequired": "Le mot de passe est requis",
          "confirmPasswordRequired": "La confirmation du mot de passe est requise",
          "passwordsMismatch": "Les mots de passe ne correspondent pas"
        }
      },
      "verifyEmail": {
        "title": "Vérifiez votre e-mail",
        "imageAlt": "Vérification de l'e-mail",
        "descriptionPrefix": "Nous avons envoyé un e-mail de vérification à",
        "descriptionSuffix": ". Veuillez cliquer sur le lien dans cet e-mail pour continuer.",
        "emailFallback": "[votre e-mail]",
        "checkInbox": "Vérifier ma boîte de réception",
        "resendEmail": "Renvoyer l'e-mail",
        "resendingEmail": "Envoi en cours..."
      },
      "twoFactor": {
        "setup": {
          "title": "Authentification multi-facteurs",
          "description": "Utilisez votre application d'authentification pour scanner ce code QR. Si vous n'avez pas d'application d'authentification sur votre appareil, vous devrez en installer une maintenant.",
          "learnMore": "En savoir plus",
          "cantScanQr": "Impossible de scanner le code QR ?",
          "qrPlaceholder": "Le code QR apparaîtra ici",
          "manualEntryKey": "Clé de saisie manuelle :",
          "cancelButton": "Annuler",
          "nextButton": "Suivant",
          "loadingButton": "Chargement..."
        },
        "verify": {
          "title": "Entrez votre code de vérification",
          "description": "Entrez le code que vous voyez dans votre application d'authentification",
          "verifyButton": "Vérifier",
          "verifyingButton": "Vérification...",
          "helpText": "Vous pouvez aussi coller votre code à 6 chiffres"
        },
        "complete": {
          "title": "Vous êtes prêt",
          "descriptionLine1": "Vous pouvez maintenant utiliser l'application d'authentification mobile pour obtenir un code d'authentification chaque fois que vous vous connectez.",
          "descriptionLine2": "Enregistrez ces codes de secours à usage unique dans un endroit sûr.",
          "copyButton": "Copier",
          "copiedButton": "Copié !",
          "downloadButton": "Télécharger",
          "footerText": "Ces codes de secours vous permettent de vous connecter si vous ne pouvez pas recevoir de SMS ou si vous n'avez pas accès à vos autres méthodes d'authentification à deux facteurs.",
          "doneButton": "Terminé",
          "loadingButton": "Chargement...",
          "copyCodesButton": "Copier les codes",
          "copiedCodesButton": "Copié !"
        }
      }
    }
  }
}
  1. Wrap your app with the I18nProvider and pass your translations:
import { ThemeProvider, I18nProvider, defaultTheme } from 'topiray-auth-react'
import fr from './i18n/fr.json'

function App() {
  return (
    <ThemeProvider theme={defaultTheme}>
      <I18nProvider translations={fr}>
        {/* Your auth components */}
      </I18nProvider>
    </ThemeProvider>
  )
}

Partial Translations

You only need to provide the keys you want to override. Any missing keys will automatically fall back to the default English translations:

import { I18nProvider } from 'topiray-auth-react'

const customTranslations = {
  topiray: {
    auth: {
      signIn: {
        title: "Welcome back",
        submitButton: "Log in"
      }
    }
  }
}

<I18nProvider translations={customTranslations}>
  {/* SignInForm will show "Welcome back" and "Log in", 
      all other strings fall back to English */}
</I18nProvider>

Dynamic Language Switching

Switch languages at runtime by updating the translations passed to I18nProvider:

import { I18nProvider, en } from 'topiray-auth-react'
import fr from './i18n/fr.json'
import de from './i18n/de.json'

const languages = { en, fr, de }

function App() {
  const [locale, setLocale] = useState('en')

  return (
    <I18nProvider translations={languages[locale]}>
      <select onChange={(e) => setLocale(e.target.value)} value={locale}>
        <option value="en">English</option>
        <option value="fr">Français</option>
        <option value="de">Deutsch</option>
      </select>
      {/* Your auth components */}
    </I18nProvider>
  )
}

Translation Keys Reference

All translation keys are nested under topiray.auth. Here is the full key structure:

| Namespace | Keys | |-----------|------| | topiray.auth.signIn | title, emailPlaceholder, passwordPlaceholder, submitButton, submittingButton, dividerText, forgotPassword, noAccount, signUpLink | | topiray.auth.signUp | title, emailPlaceholder, passwordPlaceholder, submitButton, submittingButton, dividerText, hasAccount, signInLink | | topiray.auth.forgottenPassword | title, description, emailPlaceholder, submitButton, submittingButton | | topiray.auth.resetPassword | title, description, newPasswordPlaceholder, confirmPasswordPlaceholder, submitButton, submittingButton, errors.passwordRequired, errors.confirmPasswordRequired, errors.passwordsMismatch | | topiray.auth.verifyEmail | title, imageAlt, descriptionPrefix, descriptionSuffix, emailFallback, checkInbox, resendEmail, resendingEmail | | topiray.auth.twoFactor.setup | title, description, learnMore, cantScanQr, qrPlaceholder, manualEntryKey, cancelButton, nextButton, loadingButton | | topiray.auth.twoFactor.verify | title, description, verifyButton, verifyingButton, helpText | | topiray.auth.twoFactor.complete | title, descriptionLine1, descriptionLine2, copyButton, copiedButton, downloadButton, footerText, doneButton, loadingButton, copyCodesButton, copiedCodesButton |

Browser Support

  • Chrome 90+
  • Firefox 88+
  • Safari 14+
  • Edge 90+

Authors

JayArrowz

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Submit a pull request

Support

For questions and support:

  • Create an issue on GitHub
  • Check the documentation
  • Review the demo application

Built with ❤️ for the React community