Real-Time Email Validation: Implementing Instant Feedback in Forms
Build responsive user experiences with real-time email validation that provides instant feedback without compromising performance.
Table of Contents
Table of Contents
Real-time email validation enhances user experience by providing immediate feedback during form completion. Implementing effective real-time validation requires balancing accuracy, performance, and user experience considerations.
Overview
Real-time validation provides instant feedback as users type, catching errors early and improving form completion rates. Success requires fast API calls, smart debouncing, progressive validation, and clear user feedback.
Key Benefits:
- Catch typos immediately
- Reduce form abandonment
- Improve data quality
- Better user experience
- Lower support costs
Validation Flow
Multi-stage validation from syntax through DNS/MX/SMTP. All Cleariflow plans run the same API checks; free plans differ only in monthly quota and rate limits.
type ValidationLevel = 'syntax' | 'domain' | 'mx' | 'smtp'
interface ValidationResult {
level: ValidationLevel
valid: boolean
message: string
suggestions?: string[]
}
class ProgressiveValidator {
async validate(email: string, maxLevel: ValidationLevel = 'smtp'): Promise<ValidationResult[]> {
const results: ValidationResult[] = []
// Level 1: Syntax (instant, client-side)
const syntaxResult = this.validateSyntax(email)
results.push(syntaxResult)
if (!syntaxResult.valid || maxLevel === 'syntax') return results
// Level 2: Domain exists (fast, ~50ms)
const domainResult = await this.validateDomain(email)
results.push(domainResult)
if (!domainResult.valid || maxLevel === 'domain') return results
// Level 3: MX records (medium, ~200ms)
const mxResult = await this.validateMX(email)
results.push(mxResult)
if (!mxResult.valid || maxLevel === 'mx') return results
// Level 4: SMTP verification (slow, ~2s)
const smtpResult = await this.validateSMTP(email)
results.push(smtpResult)
return results
}
private validateSyntax(email: string): ValidationResult {
const regex = /^[^s@]+@[^s@]+.[^s@]+$/
const valid = regex.test(email)
return {
level: 'syntax',
valid,
message: valid ? 'Valid format' : 'Invalid email format',
suggestions: !valid ? this.suggestCorrections(email) : undefined
}
}
private async validateDomain(email: string): Promise<ValidationResult> {
const domain = email.split('@')[1]
// Check if domain exists
return {
level: 'domain',
valid: true,
message: 'Domain exists'
}
}
private async validateMX(email: string): Promise<ValidationResult> {
// Check MX records
return {
level: 'mx',
valid: true,
message: 'Mail server configured'
}
}
private async validateSMTP(email: string): Promise<ValidationResult> {
// SMTP verification
return {
level: 'smtp',
valid: true,
message: 'Mailbox exists'
}
}
private suggestCorrections(email: string): string[] {
const common = ['gmail.com', 'yahoo.com', 'outlook.com', 'hotmail.com']
const domain = email.split('@')[1] || ''
return common
.filter(c => this.levenshtein(domain, c) <= 2)
.map(c => email.replace(domain, c))
}
private levenshtein(a: string, b: string): number {
const matrix = []
for (let i = 0; i <= b.length; i++) {
matrix[i] = [i]
}
for (let j = 0; j <= a.length; j++) {
matrix[0][j] = j
}
for (let i = 1; i <= b.length; i++) {
for (let j = 1; j <= a.length; j++) {
if (b.charAt(i - 1) === a.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1]
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j] + 1
)
}
}
}
return matrix[b.length][a.length]
}
}
Debouncing and Performance
Optimize API calls with smart debouncing.
class DebouncedValidator {
private timeouts: Map<string, NodeJS.Timeout> = new Map()
private cache: Map<string, ValidationResult[]> = new Map()
validate(
email: string,
callback: (results: ValidationResult[]) => void,
delay: number = 500
): void {
// Check cache first
const cached = this.cache.get(email)
if (cached) {
callback(cached)
return
}
// Clear existing timeout for this field
const existingTimeout = this.timeouts.get('email')
if (existingTimeout) {
clearTimeout(existingTimeout)
}
// Set new timeout
const timeout = setTimeout(async () => {
const validator = new ProgressiveValidator()
const results = await validator.validate(email, 'mx')
this.cache.set(email, results)
callback(results)
}, delay)
this.timeouts.set('email', timeout)
}
}
User Feedback
Provide clear, actionable feedback.
interface FeedbackConfig {
showInline: boolean
showIcon: boolean
suggestCorrections: boolean
validationDelay: number
}
class ValidationUI {
private config: FeedbackConfig = {
showInline: true,
showIcon: true,
suggestCorrections: true,
validationDelay: 500
}
displayFeedback(input: HTMLInputElement, results: ValidationResult[]): void {
const latestResult = results[results.length - 1]
// Update input styling
input.classList.remove('valid', 'invalid')
input.classList.add(latestResult.valid ? 'valid' : 'invalid')
// Show message
const messageEl = document.getElementById(`${input.id}-feedback`)
if (messageEl) {
messageEl.textContent = latestResult.message
messageEl.className = latestResult.valid ? 'success' : 'error'
}
// Show suggestions if available
if (latestResult.suggestions && latestResult.suggestions.length > 0) {
this.showSuggestions(input, latestResult.suggestions)
}
}
private showSuggestions(input: HTMLInputElement, suggestions: string[]): void {
// Create suggestion dropdown
console.log('Suggestions:', suggestions)
}
}
API Integration
Integrate the Cleariflow Email Validation API on blur or submit — not on every keystroke for SMTP-heavy checks.
interface CleariflowEmailValidationResponse {
email: string
autocorrect: string
deliverability: 'DELIVERABLE' | 'UNDELIVERABLE' | 'UNKNOWN'
quality_score: number
is_valid_format: { value: boolean | null; text: string }
is_disposable_email: { value: boolean | null; text: string }
is_mx_found: { value: boolean | null; text: string }
is_smtp_valid: { value: boolean | null; text: string }
}
class EmailValidationAPI {
constructor(
private apiKey: string,
private endpoint = 'https://emailvalidation.cleariflow.com/v1/'
) {}
async validate(email: string): Promise<CleariflowEmailValidationResponse> {
const url = new URL(this.endpoint)
url.searchParams.set('api_key', this.apiKey)
url.searchParams.set('email', email)
const response = await fetch(url)
if (!response.ok) {
throw new Error(`Validation failed: ${response.status}`)
}
return response.json()
}
}
// Usage: treat DELIVERABLE as confirmed when SMTP succeeds.
// UNKNOWN on Gmail/Yahoo is common — do not block signup solely on SMTP failure.
const api = new EmailValidationAPI(process.env.CLEARIFLOW_API_KEY!)
const result = await api.validate('user@example.com')
if (result.is_disposable_email.value) {
// block disposable
} else if (result.deliverability === 'UNDELIVERABLE') {
// block invalid format / no MX
} else if (result.autocorrect) {
// offer typo fix: result.autocorrect
}
Client-Side Caching
Cache validation results to reduce API calls.
class ValidationCache {
private cache: Map<string, { result: any; expiry: number }> = new Map()
private ttl: number = 300000 // 5 minutes
get(email: string): any | null {
const cached = this.cache.get(email)
if (!cached || Date.now() > cached.expiry) return null
return cached.result
}
set(email: string, result: any): void {
this.cache.set(email, {
result,
expiry: Date.now() + this.ttl
})
}
}
Conclusion
Real-time email validation requires progressive validation, smart debouncing, clear user feedback, efficient API integration, and client-side caching. Success depends on balancing validation depth with response time, providing helpful suggestions, and optimizing API usage.
Key success factors include implementing debounced validation to reduce API calls, using progressive validation levels, caching results, providing typo suggestions, and delivering instant syntax validation client-side.
Implement real-time email validation with our fast, accurate APIs, designed for instant feedback and excellent user experience.