React Email Validation

Complete guide to email validation in React

What You'll Learn

  • Install and configure VerifyForge in React
  • Validate emails in forms with real-time feedback
  • Handle validation errors gracefully
  • Implement bulk email validation
  • Best practices for production

Prerequisites

  • React 18+ installed
  • Basic knowledge of React hooks
  • VerifyForge API key (get free key)

Installation

npm install @verifyforge/sdk

Quick Start: Single Email Validation

import { useState } from 'react';
import { VerifyForge } from '@verifyforge/sdk';

const client = new VerifyForge({ apiKey: 'your_api_key' });

function EmailValidator() {
  const [email, setEmail] = useState('');
  const [result, setResult] = useState(null);
  const [loading, setLoading] = useState(false);

  const validateEmail = async () => {
    setLoading(true);
    try {
      const validation = await client.validate(email);
      setResult(validation.data);
    } catch (error) {
      console.error('Validation failed:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter email"
      />
      <button onClick={validateEmail} disabled={loading}>
        {loading ? 'Validating...' : 'Validate'}
      </button>

      {result && (
        <div>
          <p>Valid: {result.isValid ? '✓' : '✗'}</p>
          <p>Reachability: {result.reachability}</p>
        </div>
      )}
    </div>
  );
}

Tutorial 1: Form Validation with Custom Hook

Step 1: Create useEmailValidation Hook

import { useState, useCallback } from 'react';
import { VerifyForge } from '@verifyforge/sdk';

const client = new VerifyForge({
  apiKey: process.env.REACT_APP_VERIFYFORGE_API_KEY
});

export function useEmailValidation() {
  const [isValidating, setIsValidating] = useState(false);
  const [validationResult, setValidationResult] = useState(null);
  const [error, setError] = useState(null);

  const validate = useCallback(async (email) => {
    setIsValidating(true);
    setError(null);

    try {
      const result = await client.validate(email);
      setValidationResult(result.data);
      return result.data;
    } catch (err) {
      setError(err.message);
      throw err;
    } finally {
      setIsValidating(false);
    }
  }, []);

  const reset = useCallback(() => {
    setValidationResult(null);
    setError(null);
  }, []);

  return {
    validate,
    reset,
    isValidating,
    validationResult,
    error,
  };
}

Step 2: Create Registration Form

import { useState } from 'react';
import { useEmailValidation } from './useEmailValidation';

function RegistrationForm() {
  const [email, setEmail] = useState('');
  const [name, setName] = useState('');
  const { validate, isValidating, validationResult, error } = useEmailValidation();

  const handleEmailBlur = async () => {
    if (email) {
      await validate(email);
    }
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    // Validate email before submission
    const result = await validate(email);

    if (!result.isValid) {
      alert('Please enter a valid email address');
      return;
    }

    if (result.reachability === 'risky') {
      const confirm = window.confirm(
        'This email appears risky. Continue anyway?'
      );
      if (!confirm) return;
    }

    // Submit form
    console.log('Form submitted:', { name, email });
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="name">Name</label>
        <input
          id="name"
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
          required
        />
      </div>

      <div>
        <label htmlFor="email">Email</label>
        <input
          id="email"
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          onBlur={handleEmailBlur}
          required
        />

        {isValidating && <span>Validating...</span>}

        {validationResult && (
          <div className={validationResult.isValid ? 'success' : 'error'}>
            {validationResult.isValid ? (
              <span>✓ Email is valid</span>
            ) : (
              <span>✗ Email is invalid</span>
            )}
          </div>
        )}

        {error && <div className="error">{error}</div>}
      </div>

      <button type="submit" disabled={isValidating}>
        Register
      </button>
    </form>
  );
}

Step 3: Add Real-time Validation

import { useState, useEffect } from 'react';
import { useEmailValidation } from './useEmailValidation';
import { useDebounce } from './useDebounce'; // Custom debounce hook

function RealTimeEmailInput() {
  const [email, setEmail] = useState('');
  const debouncedEmail = useDebounce(email, 500);
  const { validate, isValidating, validationResult } = useEmailValidation();

  useEffect(() => {
    if (debouncedEmail && debouncedEmail.includes('@')) {
      validate(debouncedEmail);
    }
  }, [debouncedEmail, validate]);

  return (
    <div>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter email for real-time validation"
      />

      {isValidating && <span>⏳ Checking...</span>}

      {validationResult && (
        <div>
          {validationResult.isValid ? (
            <span style={{ color: 'green' }}>✓ Valid email</span>
          ) : (
            <span style={{ color: 'red' }}>✗ Invalid email</span>
          )}

          {validationResult.suggestion && (
            <p>Did you mean: {validationResult.suggestion}?</p>
          )}
        </div>
      )}
    </div>
  );
}

// useDebounce.js
import { useState, useEffect } from 'react';

export function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(handler);
  }, [value, delay]);

  return debouncedValue;
}

Tutorial 2: Bulk Email Validation

import { useState } from 'react';
import { VerifyForge } from '@verifyforge/sdk';

const client = new VerifyForge({ apiKey: process.env.REACT_APP_VERIFYFORGE_API_KEY });

function BulkEmailValidator() {
  const [emails, setEmails] = useState('');
  const [results, setResults] = useState(null);
  const [loading, setLoading] = useState(false);

  const validateBulk = async () => {
    setLoading(true);

    // Split emails by newline or comma
    const emailList = emails
      .split(/[\n,]/)
      .map(e => e.trim())
      .filter(e => e);

    try {
      const result = await client.validateBulk(emailList);
      setResults(result.data);
    } catch (error) {
      console.error('Bulk validation failed:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <h2>Bulk Email Validation</h2>

      <textarea
        rows={10}
        value={emails}
        onChange={(e) => setEmails(e.target.value)}
        placeholder="Enter emails (one per line or comma-separated)"
      />

      <button onClick={validateBulk} disabled={loading}>
        {loading ? 'Validating...' : 'Validate Emails'}
      </button>

      {results && (
        <div>
          <h3>Results</h3>
          <p>Total: {results.summary.total}</p>
          <p>Valid: {results.summary.valid}</p>
          <p>Invalid: {results.summary.invalid}</p>

          <table>
            <thead>
              <tr>
                <th>Email</th>
                <th>Status</th>
                <th>Reachability</th>
              </tr>
            </thead>
            <tbody>
              {results.results.map((item, index) => (
                <tr key={index}>
                  <td>{item.email}</td>
                  <td>{item.isValid ? '✓' : '✗'}</td>
                  <td>{item.reachable}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );
}

Tutorial 3: Error Handling

import { VerifyForge, AuthenticationError, InsufficientCreditsError } from '@verifyforge/sdk';

const client = new VerifyForge({ apiKey: process.env.REACT_APP_VERIFYFORGE_API_KEY });

function EmailValidatorWithErrorHandling() {
  const [email, setEmail] = useState('');
  const [status, setStatus] = useState({ type: null, message: '' });

  const validateEmail = async () => {
    try {
      const result = await client.validate(email);

      if (result.data.isValid) {
        setStatus({ type: 'success', message: 'Email is valid!' });
      } else {
        setStatus({ type: 'error', message: 'Email is invalid' });
      }
    } catch (error) {
      if (error instanceof AuthenticationError) {
        setStatus({ type: 'error', message: 'Invalid API key' });
      } else if (error instanceof InsufficientCreditsError) {
        setStatus({ type: 'error', message: 'Out of credits. Please upgrade.' });
      } else {
        setStatus({ type: 'error', message: 'Validation failed. Please try again.' });
      }
    }
  };

  return (
    <div>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      <button onClick={validateEmail}>Validate</button>

      {status.type && (
        <div className={status.type}>
          {status.message}
        </div>
      )}
    </div>
  );
}

Best Practices for Production

1. Environment Variables

Store your API key securely:

# .env
REACT_APP_VERIFYFORGE_API_KEY=your_api_key_here
const client = new VerifyForge({
  apiKey: process.env.REACT_APP_VERIFYFORGE_API_KEY
});

2. Cache Validation Results

import { useState, useCallback } from 'react';

function useCachedValidation() {
  const [cache, setCache] = useState(new Map());

  const validate = useCallback(async (email) => {
    // Check cache first
    if (cache.has(email)) {
      return cache.get(email);
    }

    // Validate and cache result
    const result = await client.validate(email);
    setCache(prev => new Map(prev).set(email, result.data));

    return result.data;
  }, [cache]);

  return { validate };
}

3. Debounce Input

Always debounce real-time validation to avoid excessive API calls:

const debouncedEmail = useDebounce(email, 500); // 500ms delay

4. Handle Suggestions

if (validationResult.suggestion) {
  const useSuggestion = window.confirm(
    `Did you mean ${validationResult.suggestion}?`
  );
  if (useSuggestion) {
    setEmail(validationResult.suggestion);
  }
}

Complete Example: Newsletter Signup

import { useState } from 'react';
import { VerifyForge } from '@verifyforge/sdk';

const client = new VerifyForge({ apiKey: process.env.REACT_APP_VERIFYFORGE_API_KEY });

function NewsletterSignup() {
  const [email, setEmail] = useState('');
  const [status, setStatus] = useState('idle');
  const [message, setMessage] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    setStatus('validating');

    try {
      const result = await client.validate(email);

      if (!result.data.isValid) {
        setStatus('error');
        setMessage('Please enter a valid email address');
        return;
      }

      if (result.data.disposable) {
        setStatus('error');
        setMessage('Temporary email addresses are not allowed');
        return;
      }

      if (result.data.reachability === 'invalid') {
        setStatus('error');
        setMessage('This email address cannot receive mail');
        return;
      }

      // Submit to backend
      await fetch('/api/newsletter/subscribe', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email }),
      });

      setStatus('success');
      setMessage('Successfully subscribed!');
      setEmail('');
    } catch (error) {
      setStatus('error');
      setMessage('Subscription failed. Please try again.');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="your@email.com"
        required
        disabled={status === 'validating'}
      />

      <button type="submit" disabled={status === 'validating'}>
        {status === 'validating' ? 'Subscribing...' : 'Subscribe'}
      </button>

      {message && (
        <div className={status === 'success' ? 'success' : 'error'}>
          {message}
        </div>
      )}
    </form>
  );
}

Troubleshooting

Problem: CORS errors in development

Solution: The VerifyForge API supports CORS. Ensure you're using the correct API key.

Problem: Too many API calls

Solution: Implement debouncing and caching as shown in best practices.

Problem: Slow validation

Solution: Use real-time validation only on blur, not on every keystroke.

Next Steps