Skip to main content
The Email OTP screen is displayed when users authenticate via email. It prompts users to enter the one-time passcode (OTP) sent to their email address.
The Email OTP screen automatically appears after a user enters their email address in the login modal.

How it works

When a user selects email authentication:
  1. User enters their email address in the login modal
  2. MoonKey sends a one-time passcode to the email
  3. The Email OTP screen appears, prompting the user to enter the code
  4. User enters the 6-digit code
  5. Upon successful verification, the user is authenticated

Customizing the Email OTP screen

You can customize the Email OTP experience by configuring the emailOtp prop in your MoonKeyProvider:
import { MoonKeyProvider } from '@moon-key/react-auth';

export default function App() {
  return (
    <MoonKeyProvider
      publishableKey="your_publishable_key"
      config={{
        loginMethods: ['email'],
        emailOtp: {
          skipVerifiedSuccess: false,
          expiresIn: 600,
          verifiedDisplayTime: 3000,
          verifyEmailTitle: 'Verify your email',
          verifyEmailResendTitle: 'Didn\'t receive a code?',
          resendCodeTime: 30,
          verifiedNewUserMessage: 'Email verified! Setting up your account...',
          verifiedExistingUserMessage: 'Welcome back! Signing you in...',
          verifiedSuccessMessage: 'Success! You are now signed in.'
        },
        appearance: {
          logo: 'https://your-app.com/logo.png',
          loginHeaderTitle: 'Welcome',
          loginHeaderDescription: 'Check your email for the verification code'
        }
      }}
    >
      {/* Your app */}
    </MoonKeyProvider>
  );
}

Configuration options

Email OTP behavior

skipVerifiedSuccess
boolean
default:false
Skip showing the success screen after email verification and immediately proceed to the application.When true, users won’t see a success message and will be taken directly to your app after entering the correct OTP.Example:
emailOtp: {
  skipVerifiedSuccess: true // Skip success screen, authenticate immediately
}
expiresIn
number
default:600
Time in seconds before the OTP expires.Default: 600 (10 minutes)Example:
emailOtp: {
  expiresIn: 300 // OTP expires after 5 minutes
}
verifiedDisplayTime
number
default:3000
Time in milliseconds to display the success message before closing the modal.Only applies when skipVerifiedSuccess is false.Default: 3000 (3 seconds)Example:
emailOtp: {
  verifiedDisplayTime: 2000 // Show success message for 2 seconds
}
resendCodeTime
number
default:30
Time in seconds before users can request a new OTP code.Default: 30 (30 seconds)Example:
emailOtp: {
  resendCodeTime: 60 // Users must wait 60 seconds before resending
}

UI customization

verifyEmailTitle
string
default:"Verify your email"
Title text displayed on the email verification screen.Example:
emailOtp: {
  verifyEmailTitle: 'Check your email'
}
verifyEmailResendTitle
string
default:"Didn't receive a code?"
Title text for the resend code prompt.Example:
emailOtp: {
  verifyEmailResendTitle: 'Need a new code?'
}
verifiedNewUserMessage
string
default:"Email verified!"
Success message shown to new users after verification.Displayed when a user successfully verifies their email for the first time.Example:
emailOtp: {
  verifiedNewUserMessage: 'Account created successfully! Welcome aboard.'
}
verifiedExistingUserMessage
string
default:"Welcome back!"
Success message shown to existing users after verification.Displayed when a returning user successfully verifies their email.Example:
emailOtp: {
  verifiedExistingUserMessage: 'Great to see you again! Signing you in...'
}
verifiedSuccessMessage
string
default:"Success!"
Generic success message shown after verification.This is a fallback message when MoonKey cannot determine if the user is new or existing.Example:
emailOtp: {
  verifiedSuccessMessage: 'Verification complete!'
}

Common configurations

Quick verification (skip success screen)

For a streamlined experience, skip the success screen and authenticate users immediately:
<MoonKeyProvider
  publishableKey="your_publishable_key"
  config={{
    loginMethods: ['email'],
    emailOtp: {
      skipVerifiedSuccess: true
    }
  }}
>
  {children}
</MoonKeyProvider>

Custom timing

Adjust OTP expiration and resend cooldown for your security requirements:
<MoonKeyProvider
  publishableKey="your_publishable_key"
  config={{
    loginMethods: ['email'],
    emailOtp: {
      expiresIn: 300, // 5 minutes until expiration
      resendCodeTime: 60, // 60 seconds before allowing resend
      verifiedDisplayTime: 2000 // 2 seconds success message
    }
  }}
>
  {children}
</MoonKeyProvider>

Custom messaging

Personalize the verification flow with custom messages:
<MoonKeyProvider
  publishableKey="your_publishable_key"
  config={{
    loginMethods: ['email'],
    emailOtp: {
      verifyEmailTitle: 'Enter verification code',
      verifyEmailResendTitle: 'Need a new code?',
      verifiedNewUserMessage: '🎉 Account created! Setting up your wallet...',
      verifiedExistingUserMessage: '👋 Welcome back! Logging you in...',
      verifiedSuccessMessage: '✅ Verified successfully!'
    }
  }}
>
  {children}
</MoonKeyProvider>

Enterprise security settings

For enterprise applications requiring stricter security:
<MoonKeyProvider
  publishableKey="your_publishable_key"
  config={{
    loginMethods: ['email'],
    emailOtp: {
      expiresIn: 180, // 3 minutes (shorter expiration)
      resendCodeTime: 120, // 2 minutes before resend (prevent spam)
      skipVerifiedSuccess: false, // Show success confirmation
      verifiedDisplayTime: 2000
    }
  }}
>
  {children}
</MoonKeyProvider>

General appearance customization

In addition to Email OTP-specific settings, you can customize the general appearance that applies to all UI components, including the Email OTP screen:
<MoonKeyProvider
  publishableKey="your_publishable_key"
  config={{
    loginMethods: ['email'],
    emailOtp: {
      verifyEmailTitle: 'Enter your code',
      verifiedNewUserMessage: 'Welcome! Your account is ready.'
    },
    appearance: {
      logo: 'https://your-app.com/logo.png',
      loginHeaderTitle: 'Verify Your Email',
      loginHeaderDescription: 'We sent a 6-digit code to your email',
      hideClose: false
    }
  }}
>
  {children}
</MoonKeyProvider>
Learn more about configuring appearance.

Complete example

Here’s a complete example with all Email OTP customization options:
'use client';
import { MoonKeyProvider } from '@moon-key/react-auth';

export default function App() {
  return (
    <MoonKeyProvider
      publishableKey="your_publishable_key"
      config={{
        loginMethods: ['email', 'google'],
        embeddedWallets: {
          createOnLogin: 'always'
        },
        emailOtp: {
          // Behavior
          skipVerifiedSuccess: false,
          expiresIn: 600, // 10 minutes
          verifiedDisplayTime: 3000, // 3 seconds
          resendCodeTime: 30, // 30 seconds
          
          // UI Text
          verifyEmailTitle: 'Check your email',
          verifyEmailResendTitle: 'Didn\'t receive the code?',
          
          // Success messages
          verifiedNewUserMessage: 'Email verified! Setting up your account...',
          verifiedExistingUserMessage: 'Welcome back! Signing you in...',
          verifiedSuccessMessage: 'Success! You are now signed in.'
        },
        appearance: {
          logo: 'https://myapp.com/logo.png',
          loginHeaderTitle: 'Verify Your Email',
          loginHeaderDescription: 'Enter the 6-digit code sent to your email',
          hideClose: false
        }
      }}
    >
      {/* Your app */}
    </MoonKeyProvider>
  );
}

User experience flow

Here’s what the complete email authentication flow looks like with Email OTP:
1

User initiates login

User clicks “Sign in” button, opening the login modal.
2

Enter email address

User selects email authentication and enters their email address.
3

Email OTP screen appears

The Email OTP screen appears, prompting the user to enter the code.
4

Check email

User checks their email inbox for the 6-digit verification code.
5

Enter verification code

User enters the code on the Email OTP screen.
6

Verification & authentication

MoonKey verifies the code. If valid:
  • Shows success message (unless skipVerifiedSuccess: true)
  • Authenticates the user
  • Creates embedded wallet (if createOnLogin: 'always')
  • Closes the modal and returns user to the app

Resending codes

If a user doesn’t receive the OTP or it expires, they can request a new code:
  1. User clicks “Resend code” link on the Email OTP screen
  2. MoonKey enforces the resendCodeTime cooldown
  3. A new OTP is sent to the user’s email
  4. User enters the new code
The resend cooldown prevents abuse and protects against spam attacks.

Handling errors

The Email OTP screen automatically handles common errors:
  • Invalid code - Shows an error message and allows the user to try again
  • Expired code - Prompts the user to request a new code
  • Too many attempts - May temporarily lock verification (contact support if this occurs)

Best practices

  • Consumer apps: 5-10 minutes is typical
  • Enterprise apps: 3-5 minutes for higher security
  • Consider your users’ email access patterns
  • Recommended: 30-60 seconds
  • Prevents spam while allowing legitimate resend requests
  • Too short: risk of abuse
  • Too long: frustrates users
  • Differentiate between new and returning users
  • Use friendly, encouraging language
  • Consider your brand voice
  • For faster authentication, set skipVerifiedSuccess: true
  • Best for apps where speed is critical
  • Still provide feedback in your app after authentication
  • Test with various email providers (Gmail, Outlook, etc.)
  • Check spam folders to ensure delivery
  • Verify timing configurations work as expected
  • Test resend functionality

TypeScript interface

For TypeScript users, here’s the complete EmailOtpConfig interface:
interface EmailOtpConfig {
  skipVerifiedSuccess?: boolean;
  expiresIn?: number;
  verifiedDisplayTime?: number;
  verifyEmailTitle?: string;
  verifyEmailResendTitle?: string;
  resendCodeTime?: number;
  verifiedNewUserMessage?: string;
  verifiedExistingUserMessage?: string;
  verifiedSuccessMessage?: string;
}
All fields are optional with sensible defaults.

Next steps