# How to Resend a Verification Email in Firebase

- Tool: Firebase
- Difficulty: Beginner
- Time required: 8-10 min
- Compatibility: Firebase JS SDK v9+, Firebase Auth (all plans)
- Last updated: March 2026

## TL;DR

Resend a verification email in Firebase by calling sendEmailVerification() on the currently signed-in user object. Firebase rate-limits verification emails to a maximum of 5 per email address per hour. Before resending, check user.emailVerified to confirm the email is not already verified, and use user.reload() to refresh the auth state from the server. Always wrap the call in try/catch to handle rate limit errors gracefully.

## Resending Email Verification in Firebase Auth

When a user signs up with email and password, Firebase can send a verification email to confirm their address. Sometimes users miss or delete the original email and need it resent. This tutorial shows how to resend the verification email, check the current verification status, handle rate limits, and customize the redirect URL after verification.

## Before you start

- A Firebase project with Authentication enabled
- Email/password sign-in method enabled in Firebase Console
- Firebase JS SDK v9+ installed (npm install firebase)
- A user account that has signed up but not yet verified their email

## Step-by-step guide

### 1. Check if the user's email is already verified

Before attempting to resend the verification email, check the user.emailVerified property. However, this property is cached in the client-side token and may be stale. Call user.reload() first to refresh the user's profile from the server, then check emailVerified on the refreshed user object. This prevents sending unnecessary emails to already-verified users.

```
import { getAuth } from 'firebase/auth'

const auth = getAuth()

async function checkVerificationStatus() {
  const user = auth.currentUser

  if (!user) {
    console.log('No user is signed in.')
    return false
  }

  // Refresh user data from server
  await user.reload()

  // Re-fetch the user object after reload
  const refreshedUser = auth.currentUser

  if (refreshedUser?.emailVerified) {
    console.log('Email is already verified.')
    return true
  }

  console.log('Email is not yet verified.')
  return false
}
```

**Expected result:** The function returns true if the email is verified or false if it still needs verification.

### 2. Resend the verification email

Call sendEmailVerification() with the current user object to resend the verification email. This function is imported from firebase/auth and takes the user as its first argument. Firebase sends the same type of verification email as the initial one. Wrap the call in try/catch to handle errors, particularly the too-many-requests error that occurs when the rate limit is exceeded.

```
import { getAuth, sendEmailVerification } from 'firebase/auth'

const auth = getAuth()

async function resendVerificationEmail() {
  const user = auth.currentUser

  if (!user) {
    throw new Error('No user is signed in.')
  }

  await user.reload()
  if (auth.currentUser?.emailVerified) {
    console.log('Email is already verified. No need to resend.')
    return
  }

  try {
    await sendEmailVerification(user)
    console.log('Verification email sent successfully.')
  } catch (error: any) {
    if (error.code === 'auth/too-many-requests') {
      console.error('Too many requests. Please wait before trying again.')
    } else {
      console.error('Failed to send verification email:', error.message)
    }
  }
}
```

**Expected result:** The verification email is sent to the user's email address, or a clear error message is logged if rate-limited.

### 3. Customize the verification email redirect URL

Pass an actionCodeSettings object as the second argument to sendEmailVerification() to customize where the user is redirected after clicking the verification link. Set the url property to your app's URL. This is useful for directing users back to a specific page like a welcome screen or dashboard after they verify their email.

```
import { getAuth, sendEmailVerification, ActionCodeSettings } from 'firebase/auth'

const auth = getAuth()

async function resendWithCustomRedirect() {
  const user = auth.currentUser
  if (!user) return

  const actionCodeSettings: ActionCodeSettings = {
    url: 'https://yourapp.com/welcome?verified=true',
    handleCodeInApp: false
  }

  try {
    await sendEmailVerification(user, actionCodeSettings)
    console.log('Verification email sent with custom redirect.')
  } catch (error: any) {
    console.error('Error:', error.message)
  }
}
```

**Expected result:** The verification email contains a link that redirects to your specified URL after the user verifies.

### 4. Add a cooldown timer to prevent rapid resend clicks

Since Firebase rate-limits verification emails to 5 per hour per email address, implement a client-side cooldown timer to discourage users from clicking the resend button repeatedly. Disable the button for 60 seconds after each send and show a countdown. This improves user experience and avoids hitting the rate limit.

```
import { useState, useCallback } from 'react'
import { getAuth, sendEmailVerification } from 'firebase/auth'

function useResendVerification() {
  const [cooldown, setCooldown] = useState(0)
  const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle')

  const resend = useCallback(async () => {
    if (cooldown > 0) return

    const user = getAuth().currentUser
    if (!user) return

    setStatus('sending')
    try {
      await sendEmailVerification(user)
      setStatus('sent')
      setCooldown(60)

      const interval = setInterval(() => {
        setCooldown((prev) => {
          if (prev <= 1) {
            clearInterval(interval)
            return 0
          }
          return prev - 1
        })
      }, 1000)
    } catch (error: any) {
      setStatus('error')
    }
  }, [cooldown])

  return { resend, cooldown, status }
}
```

**Expected result:** The hook manages sending state and a 60-second cooldown, preventing the user from spamming the resend button.

### 5. Poll for verification completion

After the user receives and clicks the verification link, your app needs to detect the change. Since Firebase does not push verification status changes to the client in real time, you must poll by calling user.reload() periodically. Check emailVerified after each reload and redirect the user once verified.

```
import { getAuth } from 'firebase/auth'

function pollForVerification(
  onVerified: () => void,
  intervalMs: number = 3000,
  maxAttempts: number = 60
) {
  const auth = getAuth()
  let attempts = 0

  const timer = setInterval(async () => {
    attempts++
    const user = auth.currentUser
    if (!user || attempts >= maxAttempts) {
      clearInterval(timer)
      return
    }

    await user.reload()
    if (auth.currentUser?.emailVerified) {
      clearInterval(timer)
      onVerified()
    }
  }, intervalMs)

  return () => clearInterval(timer)
}
```

**Expected result:** The function polls every 3 seconds and calls the onVerified callback as soon as the user completes email verification.

## Complete code example

File: `email-verification.ts`

```typescript
import { getAuth, sendEmailVerification, ActionCodeSettings, User } from 'firebase/auth'

const auth = getAuth()

export async function isEmailVerified(): Promise<boolean> {
  const user = auth.currentUser
  if (!user) return false

  await user.reload()
  return auth.currentUser?.emailVerified ?? false
}

export async function resendVerification(
  redirectUrl?: string
): Promise<{ success: boolean; error?: string }> {
  const user = auth.currentUser
  if (!user) {
    return { success: false, error: 'No user signed in.' }
  }

  const alreadyVerified = await isEmailVerified()
  if (alreadyVerified) {
    return { success: false, error: 'Email is already verified.' }
  }

  const settings: ActionCodeSettings | undefined = redirectUrl
    ? { url: redirectUrl, handleCodeInApp: false }
    : undefined

  try {
    await sendEmailVerification(user, settings)
    return { success: true }
  } catch (error: any) {
    if (error.code === 'auth/too-many-requests') {
      return { success: false, error: 'Rate limit reached. Wait a few minutes.' }
    }
    return { success: false, error: error.message }
  }
}

export function pollForVerification(
  onVerified: () => void,
  intervalMs = 3000,
  maxAttempts = 60
): () => void {
  let attempts = 0

  const timer = setInterval(async () => {
    attempts++
    if (attempts >= maxAttempts) {
      clearInterval(timer)
      return
    }

    const verified = await isEmailVerified()
    if (verified) {
      clearInterval(timer)
      onVerified()
    }
  }, intervalMs)

  return () => clearInterval(timer)
}
```

## Common mistakes

- **Checking emailVerified without calling user.reload() first, causing stale verification status** — undefined Fix: Always call await user.reload() before reading emailVerified. The property is cached in the client-side token and does not update automatically when the user verifies on another device or tab.
- **Not handling the auth/too-many-requests error, leaving users confused when the resend silently fails** — undefined Fix: Catch the error and display a user-friendly message like 'Please wait a few minutes before requesting another verification email.' Firebase allows a maximum of 5 verification emails per hour per address.
- **Calling sendEmailVerification after the user's session has expired, causing an auth/requires-recent-login error** — undefined Fix: Ensure the user is still signed in by checking auth.currentUser before calling sendEmailVerification. If the session expired, prompt the user to sign in again.

## Best practices

- Always call user.reload() before checking emailVerified to get the latest status from the server
- Implement a client-side cooldown timer (60 seconds) to prevent users from hitting the 5-per-hour rate limit
- Show clear feedback to the user: 'Verification email sent' on success, 'Please wait' on cooldown, and helpful error messages on failure
- Use actionCodeSettings to redirect users to a specific page after verification for a smoother onboarding flow
- Poll for verification completion on the waiting screen so the UI updates automatically when the user verifies
- Add the redirect URL domain to Firebase Console Authorized Domains to prevent verification link failures
- Log verification resend events for analytics to understand how many users need multiple verification emails

## Frequently asked questions

### How many verification emails can Firebase send per hour?

Firebase rate-limits verification emails to 5 per email address per hour. If you exceed this limit, the SDK throws an auth/too-many-requests error. You cannot increase this limit — it is a fixed Firebase restriction to prevent abuse.

### Can I customize the verification email template?

Yes. Go to Firebase Console, then Authentication, then Templates. You can edit the subject line, body text, and sender name. For full HTML customization, upgrade to Identity Platform or use a custom email handler with your own SMTP service.

### Does the verification link expire?

Yes. Firebase verification links expire after 3 days by default. If the user clicks an expired link, they will see an error. You can adjust the expiration period (1 hour to 30 days) via the Firebase Admin SDK's generateEmailVerificationLink method.

### How do I know when the user has verified their email?

Firebase does not push verification status changes to the client. You must call user.reload() and then check emailVerified. Implement polling on the verification waiting screen, checking every 3-5 seconds until the status changes.

### Can I force users to verify their email before accessing the app?

Firebase Auth does not block unverified users from signing in. You must implement this check yourself: after sign-in, check user.emailVerified and redirect unverified users to a verification screen. Also enforce this in Firestore security rules with request.auth.token.email_verified.

### Can RapidDev help implement email verification flows in my Firebase app?

Yes. RapidDev can build complete email verification flows including signup, verification status checking, resend functionality, custom redirect URLs, and security rules that enforce verification before data access.

---

Source: https://www.rapidevelopers.com/firebase-tutorial/how-to-resend-verification-email-in-firebase
© RapidDev — https://www.rapidevelopers.com/firebase-tutorial/how-to-resend-verification-email-in-firebase
