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.
