Build real-time email verification into Next.js signup forms, checkout pages, and API routes. 25ms API response time means zero UX friction while blocking 94% of fake signups, catching typos, and preventing disposable emails from entering your database.
Every signup form without email validation is an open door for fake accounts, spam submissions, and data that degrades your entire system. The impact compounds with scale.
A signup form without email verification accepts 12% invalid addresses—disposable emails, typos, and fabricated entries that pollute your database, inflate user counts, and break your email sequences. Real-time verification at the form level catches these problems before submission, with 25ms API response time that adds zero perceived delay to the user experience.
Email verification isn't a single check—it's a pipeline of validations that progressively confirms an email is real, deliverable, and safe to accept. Each layer catches a different type of bad data:
Validates the email conforms to the Internet message format standard—correct structure, allowed characters, proper domain format. Catches entries like "user@" or "@domain.com"
Confirms the email domain exists and has mail exchange (MX) records configured. Catches entries like "user@nonexistent-domain.xyz"
Connects to the recipient's mail server and verifies the mailbox exists—without sending an email. Catches "john.doe@gmail.com" when the account has been deleted
Checks against a database of 5,000+ temporary email services (Mailinator, Guerrilla Mail, 10minutemail). Blocks fake signups at the source
Fuzzy matching identifies common misspellings: gmial.com, gmai.com, yaho.com, hotmal.com. Returns a suggestion for one-click correction
Combines all signals into a risk score (0-100). Free providers, catch-all domains, and role-based addresses get higher risk scores for downstream filtering
There are three integration patterns for Next.js apps, depending on where validation happens. Each has trade-offs between security, performance, and user experience.
The most secure approach. Validation runs on your server before data reaches your database. The email never leaves your infrastructure until it's confirmed valid. Use this for signup form submissions, checkout forms, and any data that gets persisted.
// app/api/validate/route.ts
import { NextRequest, NextResponse } from 'next/server';
interface ValidationResponse {
email: string;
status: 'deliverable' | 'undeliverable' | 'unknown' | 'accept_all';
suggestion?: string;
disposable: boolean;
roleBased: boolean;
freeProvider: boolean;
mxRecords: boolean;
smtpCheck: boolean;
riskScore: number;
}
export async function POST(request: NextRequest) {
const { email } = await request.json();
if (!email || typeof email !== 'string') {
return NextResponse.json(
{ error: 'Email is required' },
{ status: 400 }
);
}
const response = await fetch(
'https://api.email-check.app/v1/validate',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.EMAIL_CHECK_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
}
);
const result: ValidationResponse = await response.json();
// Block disposable and role-based emails at submission
if (result.disposable || result.roleBased) {
return NextResponse.json({
valid: false,
reason: result.disposable
? 'disposable_email'
: 'role_based_email'
});
}
// Allow catch-all and accept_all with a warning
if (result.status === 'accept_all' || result.status === 'unknown') {
return NextResponse.json({
valid: true,
email: result.suggestion || email,
warning: 'delivery_not_guaranteed',
riskScore: result.riskScore
});
}
return NextResponse.json({
valid: result.status === 'deliverable',
email: result.suggestion || email,
riskScore: result.riskScore
});
}Real-time feedback as users type. Validates on each typing pause (debounced at 400ms) and shows inline results: green checkmark for valid, error for invalid, and suggestion button for typos. This pattern provides the best UX but requires exposing your API key through a server proxy.
// components/email-verification-input.tsx
'use client';
import { useState, useCallback, useRef } from 'react';
interface ValidationResult {
valid: boolean;
email: string;
suggestion?: string;
warning?: string;
reason?: string;
}
export function EmailVerificationInput({
value,
onChange,
onSubmit
}: {
value: string;
onChange: (email: string) => void;
onSubmit: () => void;
}) {
const [status, setStatus] = useState<
'idle' | 'validating' | 'valid' | 'invalid' | 'warning'
>('idle');
const [suggestion, setSuggestion] = useState('');
const [message, setMessage] = useState('');
const debounceRef = useRef<NodeJS.Timeout>();
const validate = useCallback(async (email: string) => {
if (!email.includes('@')) {
setStatus('idle');
return;
}
setStatus('validating');
try {
const response = await fetch('/api/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
const result: ValidationResult = await response.json();
if (result.valid) {
if (result.warning) {
setStatus('warning');
setMessage('This mailbox may not receive all emails');
} else {
setStatus('valid');
setMessage('');
}
} else {
setStatus('invalid');
if (result.reason === 'disposable_email') {
setMessage('Please use a permanent email address');
} else if (result.reason === 'role_based_email') {
setMessage('Please use a personal email address');
} else {
setMessage('Please enter a valid email address');
}
}
if (result.suggestion) {
setSuggestion(result.suggestion);
}
} catch {
setStatus('idle');
}
}, []);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const email = e.target.value;
onChange(email);
setSuggestion('');
clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(
() => validate(email),
400
);
};
const acceptSuggestion = () => {
onChange(suggestion);
setSuggestion('');
validate(suggestion);
};
return (
<div className="space-y-2">
<div className="relative">
<input
type="email"
value={value}
onChange={handleChange}
placeholder="you@example.com"
className="w-full px-4 py-3 border rounded-lg
focus:outline-none focus:ring-2"
/>
{status === 'validating' && (
<span className="absolute right-3 top-3
loading loading-spinner loading-sm" />
)}
</div>
{suggestion && (
<button
type="button"
onClick={acceptSuggestion}
className="text-sm text-blue-600 hover:underline"
>
Did you mean {suggestion}?
</button>
)}
{message && status === 'invalid' && (
<p className="text-sm text-red-600">{message}</p>
)}
{message && status === 'warning' && (
<p className="text-sm text-amber-600">{message}</p>
)}
</div>
);
}For forms managed by React Hook Form (used in production apps), integrate validation as an async validator on the email field. This keeps validation logic alongside other form validation and provides consistent error handling.
// app/(auth)/signup/_components/signup-form.tsx
'use client';
import { useForm } from 'react-hook-form';
import { EmailVerificationInput } from
'@/components/email-verification-input';
interface SignupFormData {
name: string;
email: string;
password: string;
company?: string;
}
async function validateEmail(email: string): Promise<boolean> {
const response = await fetch('/api/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
const result = await response.json();
return result.valid;
}
export function SignupForm() {
const {
register,
handleSubmit,
setValue,
watch,
formState: { errors, isSubmitting }
} = useForm<SignupFormData>({
defaultValues: {
name: '',
email: '',
password: '',
company: ''
}
});
const emailValue = watch('email');
const onSubmit = async (data: SignupFormData) => {
// Double-check server-side before creating account
const isValid = await validateEmail(data.email);
if (!isValid) {
throw new Error('Invalid email address');
}
await createAccount(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
{/* Name field */}
<input
{...register('name', { required: 'Name is required' })}
placeholder="Full name"
/>
{/* Email with real-time verification */}
<div>
<input
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: 'Enter a valid email format'
},
validate: validateEmail
})}
placeholder="you@example.com"
/>
{errors.email && (
<p className="text-red-600 text-sm">
{errors.email.message}
</p>
)}
</div>
{/* Password field */}
<input
{...register('password', {
required: 'Password is required',
minLength: {
value: 8,
message: 'Password must be at least 8 characters'
}
})}
type="password"
placeholder="Create a password"
/>
<button
type="submit"
disabled={isSubmitting}
className="w-full bg-blue-600 text-white py-3
rounded-lg font-semibold disabled:opacity-50"
>
{isSubmitting ? 'Creating account...' : 'Create Account'}
</button>
</form>
);
}The Email-Check.app API returns structured data you can use to make granular decisions about each email address. Here's a typical response:
{
"email": "user@gmial.com",
"status": "undeliverable",
"suggestion": "user@gmail.com",
"disposable": false,
"roleBased": false,
"freeProvider": true,
"mxRecords": false,
"smtpCheck": false,
"riskScore": 92,
"didYouMean": "user@gmail.com",
"formatValid": true,
"domain": "gmial.com"
}Decision logic for your app:
status: "deliverable" — Accept the email, proceed with signupstatus: "undeliverable" — Block submission, show errorsuggestion present — Show "Did you mean?" with one-click correctiondisposable: true — Block with message "Use a permanent email address"roleBased: true — Block or warn (depends on your use case)status: "accept_all" — Accept with warning (catch-all domain)If you already have a user database with unverified emails, run a bulk validation pass before your next campaign. Email-Check.app supports CSV upload with results you can download and import directly:
// Bulk validation via API for existing user lists
// Pass emails as an array for batch processing
const response = await fetch(
'https://api.email-check.app/v1/validate/bulk',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.EMAIL_CHECK_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
emails: [
'user1@example.com',
'user2@gmial.com',
'user3@company.com',
'test@10minutemail.com'
]
})
}
);
const results = await response.json();
// results.status contains breakdown:
// - deliverable: emails confirmed valid
// - undeliverable: emails that bounce
// - disposable: temporary email services
// - accept_all: catch-all domains
// - unknown: could not verify
// Download cleaned list from the dashboard:
// 1. Upload your CSV (name, email columns)
// 2. Choose validation checks to run
// 3. Download results with columns:
// email, status, suggestion, riskScore, disposable
// 4. Import cleaned list back to your CRM/databaseAggressive validation hurts signup conversion if implemented poorly. Follow these patterns to protect data quality without friction:
Store your API key in environment variables and access it through Next.js built-in env support:
// .env.local (never commit this file)
EMAIL_CHECK_API_KEY=your_production_api_key_here
// For Next.js App Router, the key is automatically
// available in server components and API routes
// via process.env.EMAIL_CHECK_API_KEY
// .env.example (commit this)
EMAIL_CHECK_API_KEY=your_api_key_here
// next.config.ts - if using the key in client code
// (not recommended - use API route proxy instead)
// next.config.ts
const nextConfig = {
env: {
NEXT_PUBLIC_API_URL: process.env.EMAIL_CHECK_API_URL
}
};A B2B SaaS platform with 45,000 monthly signups integrated real-time email verification at their Next.js signup form using Pattern 2 (client-side with debounced validation). Results after 30 days:
Implementation detail: The team chose client-side debounced validation (Pattern 2) for immediate feedback during typing, combined with server-side re-validation at form submission for security. This dual approach catches both UX-level issues (typos) and programmatic abuse (bot submissions with fake emails) without adding visible latency.
| Mistake | Impact | Fix |
|---|---|---|
| Only using regex validation | Accepts fake@anything.com | Add SMTP + MX verification |
| Validating without debouncing | Excessive API calls, slow form | Debounce at 300-500ms |
| Exposing API key in client code | Security vulnerability | Use API route as proxy |
| Blocking all catch-all domains | Loses 15% of B2B signups | Accept with warning |
| Only validating at signup | Existing data degrades | Run bulk validation monthly |
Build email verification into your Next.js app in three steps:
/api/validate as a secure proxy that forwards requests to the Email-Check.app APIEmail-Check.app handles validation with 99.9% accuracy and 25ms average response time—fast enough for real-time form validation without any perceptible delay.
Professional plans start at $29/month with 6,000 validations. Set up your API key, add the validation route to your Next.js app, and block fake signups before your next deploy.
View Pricing PlansRelated guides: Learn about SaaS signup form protection strategies, real-time validation API for SaaS, and disposable email detection for fake signup prevention.
The Email-Check.app API is designed for developer integration: REST endpoints, TypeScript SDK, and response times that work at every level of your Next.js app.
Sub-30ms response time means real-time form validation with zero UX impact. Debounce at 400ms and the API call completes before users notice.
Multi-layer validation pipeline: syntax, DNS, MX records, SMTP mailbox check, disposable detection, and typo correction—all in a single API call.
Auto-generated TypeScript types from OpenAPI schema. Full type safety for request and response objects, with intellisense support in your IDE.
Returns suggested corrections for misspelled domains: gmial.com → gmail.com, yaho.com → yahoo.com. Recover 7% of leads lost to typos.
5,000+ disposable email domains updated daily. Block temporary email services at signup—prevent fake accounts and spam form submissions.
Every email gets a 0-100 risk score combining domain reputation, account type, and deliverability signals. Use risk thresholds to build custom acceptance rules.
| Feature | API Route | Client-Side | React Hook Form |
|---|---|---|---|
| Security (Key Protection) | ✓ Server-side | Needs proxy | ✓ Server-side |
| Real-Time Feedback | On submit only | ✓ As-you-type | ✓ On blur/submit |
| Typo Correction UX | ✗ No inline | ✓ Suggestion button | Custom needed |
| Form Library Compatibility | ✓ Any | ✓ Any | ✓ React Hook Form |
| Bot Protection | Partial | ✗ Bypassable | Partial |
| Recommended For | Checkouts, secure forms | Signup UX optimization | Production signup forms |
Join 3,200+ businesses using Email-Check.app to protect signup forms, prevent fake accounts, and maintain a clean user database with 99.9% validation accuracy.
Professional plans starting at $29/month for 6,000 validations. No free tier. Enterprise pricing available.