Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Resend a Verification Email in Firebase

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.

What you'll learn

  • How to resend a verification email using sendEmailVerification()
  • How to check and refresh the user's email verification status
  • How to handle Firebase's rate limits on verification emails
  • How to customize the verification email with actionCodeSettings
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read8-10 minFirebase JS SDK v9+, Firebase Auth (all plans)March 2026RapidDev Engineering Team
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.

Prerequisites

  • 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.

typescript
1import { getAuth } from 'firebase/auth'
2
3const auth = getAuth()
4
5async function checkVerificationStatus() {
6 const user = auth.currentUser
7
8 if (!user) {
9 console.log('No user is signed in.')
10 return false
11 }
12
13 // Refresh user data from server
14 await user.reload()
15
16 // Re-fetch the user object after reload
17 const refreshedUser = auth.currentUser
18
19 if (refreshedUser?.emailVerified) {
20 console.log('Email is already verified.')
21 return true
22 }
23
24 console.log('Email is not yet verified.')
25 return false
26}

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.

typescript
1import { getAuth, sendEmailVerification } from 'firebase/auth'
2
3const auth = getAuth()
4
5async function resendVerificationEmail() {
6 const user = auth.currentUser
7
8 if (!user) {
9 throw new Error('No user is signed in.')
10 }
11
12 await user.reload()
13 if (auth.currentUser?.emailVerified) {
14 console.log('Email is already verified. No need to resend.')
15 return
16 }
17
18 try {
19 await sendEmailVerification(user)
20 console.log('Verification email sent successfully.')
21 } catch (error: any) {
22 if (error.code === 'auth/too-many-requests') {
23 console.error('Too many requests. Please wait before trying again.')
24 } else {
25 console.error('Failed to send verification email:', error.message)
26 }
27 }
28}

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.

typescript
1import { getAuth, sendEmailVerification, ActionCodeSettings } from 'firebase/auth'
2
3const auth = getAuth()
4
5async function resendWithCustomRedirect() {
6 const user = auth.currentUser
7 if (!user) return
8
9 const actionCodeSettings: ActionCodeSettings = {
10 url: 'https://yourapp.com/welcome?verified=true',
11 handleCodeInApp: false
12 }
13
14 try {
15 await sendEmailVerification(user, actionCodeSettings)
16 console.log('Verification email sent with custom redirect.')
17 } catch (error: any) {
18 console.error('Error:', error.message)
19 }
20}

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.

typescript
1import { useState, useCallback } from 'react'
2import { getAuth, sendEmailVerification } from 'firebase/auth'
3
4function useResendVerification() {
5 const [cooldown, setCooldown] = useState(0)
6 const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle')
7
8 const resend = useCallback(async () => {
9 if (cooldown > 0) return
10
11 const user = getAuth().currentUser
12 if (!user) return
13
14 setStatus('sending')
15 try {
16 await sendEmailVerification(user)
17 setStatus('sent')
18 setCooldown(60)
19
20 const interval = setInterval(() => {
21 setCooldown((prev) => {
22 if (prev <= 1) {
23 clearInterval(interval)
24 return 0
25 }
26 return prev - 1
27 })
28 }, 1000)
29 } catch (error: any) {
30 setStatus('error')
31 }
32 }, [cooldown])
33
34 return { resend, cooldown, status }
35}

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.

typescript
1import { getAuth } from 'firebase/auth'
2
3function pollForVerification(
4 onVerified: () => void,
5 intervalMs: number = 3000,
6 maxAttempts: number = 60
7) {
8 const auth = getAuth()
9 let attempts = 0
10
11 const timer = setInterval(async () => {
12 attempts++
13 const user = auth.currentUser
14 if (!user || attempts >= maxAttempts) {
15 clearInterval(timer)
16 return
17 }
18
19 await user.reload()
20 if (auth.currentUser?.emailVerified) {
21 clearInterval(timer)
22 onVerified()
23 }
24 }, intervalMs)
25
26 return () => clearInterval(timer)
27}

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

Complete working example

email-verification.ts
1import { getAuth, sendEmailVerification, ActionCodeSettings, User } from 'firebase/auth'
2
3const auth = getAuth()
4
5export async function isEmailVerified(): Promise<boolean> {
6 const user = auth.currentUser
7 if (!user) return false
8
9 await user.reload()
10 return auth.currentUser?.emailVerified ?? false
11}
12
13export async function resendVerification(
14 redirectUrl?: string
15): Promise<{ success: boolean; error?: string }> {
16 const user = auth.currentUser
17 if (!user) {
18 return { success: false, error: 'No user signed in.' }
19 }
20
21 const alreadyVerified = await isEmailVerified()
22 if (alreadyVerified) {
23 return { success: false, error: 'Email is already verified.' }
24 }
25
26 const settings: ActionCodeSettings | undefined = redirectUrl
27 ? { url: redirectUrl, handleCodeInApp: false }
28 : undefined
29
30 try {
31 await sendEmailVerification(user, settings)
32 return { success: true }
33 } catch (error: any) {
34 if (error.code === 'auth/too-many-requests') {
35 return { success: false, error: 'Rate limit reached. Wait a few minutes.' }
36 }
37 return { success: false, error: error.message }
38 }
39}
40
41export function pollForVerification(
42 onVerified: () => void,
43 intervalMs = 3000,
44 maxAttempts = 60
45): () => void {
46 let attempts = 0
47
48 const timer = setInterval(async () => {
49 attempts++
50 if (attempts >= maxAttempts) {
51 clearInterval(timer)
52 return
53 }
54
55 const verified = await isEmailVerified()
56 if (verified) {
57 clearInterval(timer)
58 onVerified()
59 }
60 }, intervalMs)
61
62 return () => clearInterval(timer)
63}

Common mistakes when resending a Verification Email in Firebase

Why it's a problem: Checking emailVerified without calling user.reload() first, causing stale verification status

How to avoid: 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.

Why it's a problem: Not handling the auth/too-many-requests error, leaving users confused when the resend silently fails

How to avoid: 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.

Why it's a problem: Calling sendEmailVerification after the user's session has expired, causing an auth/requires-recent-login error

How to avoid: 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

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Firebase Auth app where users sign up with email/password. Show me how to implement a resend verification email feature using the modular SDK v9, including checking if already verified, handling rate limits, adding a cooldown timer, and polling to detect when the user completes verification.

Firebase Prompt

Build a React component for Firebase email verification that shows the verification status, a Resend button with a 60-second cooldown timer, rate limit error handling, and automatic redirect when the user completes verification. Use Firebase modular SDK v9+ with TypeScript.

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.