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.

Real-Time Email Validation: Implementing Instant Feedback in Forms
11 septembre 2025
Mis à jour le 6 juin 2026
10 min read
Email Validation

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.

Real-Time Email Validation Overview
Real-Time Email Validation Overview

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.

Tags:real-timeinstant-feedbackuser-experienceform-validation