Back to Blog
Guide2026-03-048 min readUpdated 2026-03-18

How to Integrate Email Validation Into Your Signup Form

Real-time email validation on signup is a 45-minute integration that pays for itself on day one. Here's the full React and Next.js walkthrough with every edge case handled.

MS

MailSentry Team

Email validation experts

TL;DR

  • Validate server-side via an API route to keep your API key secure
  • Debounce client-side calls by 600ms for responsive UX without API abuse
  • Fail open — if the validation service is down, accept and verify later
Integration Architecture
Client Form
API Route
MailSentry API
Database
API key stays server-side • Client never sees it

Your signup form is the front door to your product. Every email address that passes through it becomes a node in your communication infrastructure — password resets, onboarding sequences, billing notifications, and re-engagement campaigns all depend on it. If you let bad addresses through the door, everything downstream suffers.

This guide walks through integrating real-time email validation into a signup form, from the API call to the UI feedback, using React and Next.js as the reference stack. The principles apply to any framework.

Architecture Overview

The integration follows a two-layer pattern:

  1. Client-side quick check — Basic syntax validation for instant feedback as the user types.
  2. Server-side deep validation — A call to your validation API when the form is submitted, catching invalid domains, disposable addresses, typos, and risky mailboxes.

The API call happens on the server to keep your API key secure and to prevent client-side bypassing.

Step 1: The Server-Side Validation Endpoint

Create an API route that accepts an email address, validates it, and returns a structured result:

// app/api/validate-email/route.ts
import { NextResponse } from "next/server";

export async function POST(request: Request) {
  const { email } = await request.json();

  if (!email || typeof email !== "string") {
    return NextResponse.json(
      { error: "Email is required" },
      { status: 400 }
    );
  }

  try {
    const response = await fetch(
      "https://api.mailsentry.net/v1/validate",
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.MAILSENTRY_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ email }),
      }
    );

    const result = await response.json();

    return NextResponse.json({
      valid: result.is_valid,
      disposable: result.is_disposable,
      role_based: result.is_role_based,
      suggestion: result.suggestion || null,
      risk_score: result.risk_score,
    });
  } catch (error) {
    // Fail open: if validation service is down, allow signup
    // but flag for later verification
    return NextResponse.json({
      valid: true,
      disposable: false,
      role_based: false,
      suggestion: null,
      risk_score: null,
      unverified: true,
    });
  }
}

Note the fail-open strategy in the catch block. If your validation provider is temporarily unavailable, you do not want to block all signups. Accept the address and flag it for asynchronous re-verification.

Step 2: The Client-Side Hook

Create a custom hook that debounces the validation call and manages loading and error states:

Solve this with MailSentry

8 validation layers, real-time results, sub-50ms response.

Try MailSentry Free →
// hooks/useEmailValidation.ts
import { useState, useCallback, useRef } from "react";

type ValidationResult = {
  valid: boolean;
  disposable: boolean;
  role_based: boolean;
  suggestion: string | null;
  risk_score: number | null;
};

export function useEmailValidation() {
  const [result, setResult] = useState<ValidationResult | null>(null);
  const [loading, setLoading] = useState(false);
  const timeoutRef = useRef<NodeJS.Timeout>();

  const validate = useCallback((email: string) => {
    // Clear previous timeout
    if (timeoutRef.current) clearTimeout(timeoutRef.current);

    // Reset state for empty or obviously invalid input
    if (!email || !email.includes("@") || email.length < 5) {
      setResult(null);
      setLoading(false);
      return;
    }

    setLoading(true);

    // Debounce: wait 600ms after user stops typing
    timeoutRef.current = setTimeout(async () => {
      try {
        const response = await fetch("/api/validate-email", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ email }),
        });
        const data = await response.json();
        setResult(data);
      } catch {
        setResult(null); // fail silently on network error
      } finally {
        setLoading(false);
      }
    }, 600);
  }, []);

  return { result, loading, validate };
}

The 600ms debounce prevents firing a validation request on every keystroke while still providing near-instant feedback once the user pauses.

Step 3: The Signup Form Component

Wire the hook into your form and display contextual feedback:

// components/SignupForm.tsx
"use client";
import { useState } from "react";
import { useEmailValidation } from "@/hooks/useEmailValidation";

export function SignupForm() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const { result, loading, validate } = useEmailValidation();

  const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    setEmail(value);
    validate(value);
  };

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

    if (result && !result.valid) {
      return; // block submission
    }

    // Proceed with signup logic
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="email">Email</label>
        <input
          id="email"
          type="email"
          value={email}
          onChange={handleEmailChange}
          className={
            result
              ? result.valid
                ? "border-green-500"
                : "border-red-500"
              : ""
          }
        />

        {loading && (
          <p className="text-sm text-gray-500">Checking email...</p>
        )}

        {result && !result.valid && (
          <p className="text-sm text-red-600">
            This email address is invalid.
          </p>
        )}

        {result?.suggestion && (
          <p className="text-sm text-amber-600">
            Did you mean{" "}
            <button
              type="button"
              onClick={() => {
                setEmail(result.suggestion!);
                validate(result.suggestion!);
              }}
              className="underline"
            >
              {result.suggestion}
            </button>
            ?
          </p>
        )}

        {result?.disposable && (
          <p className="text-sm text-red-600">
            Disposable email addresses are not allowed.
          </p>
        )}
      </div>

      <div>
        <label htmlFor="password">Password</label>
        <input id="password" type="password" value={password}
          onChange={(e) => setPassword(e.target.value)} />
      </div>

      <button type="submit"
        disabled={loading || (result !== null && !result.valid)}>
        Create Account
      </button>
    </form>
  );
}

UX Best Practices

  • Do not validate on every keystroke. Debounce by at least 400-600ms to avoid hammering your API and showing flickering states.
  • Show a loading indicator. A subtle spinner or "Checking..." text reassures the user that something is happening.
  • Use color and icons, not just text. A green checkmark for valid, red border for invalid, and amber for suggestions provides fast visual feedback.
  • Never auto-correct without consent. Show the suggestion and let the user click to accept it.
  • Fail open. If the validation API is unreachable, allow the signup and validate asynchronously rather than blocking the user.
  • Keep error messages helpful. "This email address is invalid" is better than "Validation failed." If you have a suggestion, lead with it.

Key Takeaways

Integrating email validation into your signup form is a high-leverage improvement that takes a few hours to build and pays off indefinitely. Use a server-side API route to protect your credentials, a debounced client-side hook for responsive UX, and clear inline feedback to guide the user. The result is cleaner data, fewer bounces, and a smoother onboarding experience from the very first interaction.

Try MailSentry Free

8 validation layers, sub-50ms response, 1,000 checks/month free.

Get Your Free API Key →

Keep Reading

More guides and insights on email validation.

Start validating emails today

1,000 free checks every month. All 8 validation layers included. No credit card needed.