Developer Hub / Email validation in JavaScript
Email Validation in JavaScript: Regex, HTML5 and MX Checks
How to validate an email address in JavaScript, step by step. We tested three common email regex patterns on 18 real addresses, so you can see exactly what each one accepts and rejects. Then we add the HTML5 email input, a server-side MX lookup in Node.js, and the point where a regex is no longer enough.
Tested with Node.js 26 on 21 October 2026. Every code sample on this page runs as shown.
The three layers of email validation
"Validate an email address" can mean three different checks. Each one catches different problems:
- Format. Does the text look like an email address? A regex or the HTML5 email input does this. It catches typing mistakes like
name@gmailcom. - Domain. Can the domain receive email? A DNS lookup of the domain's MX records does this. It catches domains that do not exist.
- Mailbox. Does this exact mailbox exist? Only an SMTP check against the mail server can answer this. It catches addresses like
old.employee@realcompany.com, which pass the first two checks and still bounce.
JavaScript in the browser can only do layer 1. Node.js on a server can do layers 1 and 2. Layer 3 needs outbound connections on port 25, which most cloud hosts block, so it is usually done through an email verification API.
Three email regex patterns for JavaScript
Below are three patterns you can use for email validation with regex in JavaScript, from the shortest to the strictest. All three work with pattern.test(email).
Pattern 1: the short pattern
This is the pattern you will see in most answers online. It checks for one @, no spaces, and a dot after the @.
const simple = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
Pattern 2: the HTML standard pattern
The HTML standard defines the pattern that browsers use for <input type="email">. It is stricter about which characters are allowed and checks each part of the domain.
const html5 = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
Pattern 3: the practical pattern
This version starts from the HTML standard pattern and adds four rules that real mail servers apply: no dot at the start or end of the local part, no two dots in a row, at least one dot in the domain, and a top-level domain of at least two letters.
const practical = /^(?!\.)(?!.*\.\.)[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(?<!\.)@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}$/;
function isValidEmailFormat(email) {
return practical.test(email.trim());
}
The (?<!\.) part is a lookbehind. It works in every current browser and in Node.js.
Test results: 18 addresses, 3 patterns
We ran each pattern on the same 18 addresses. A green "yes" means the pattern accepts the address.
| Address | Short | HTML5 | Practical | Should it pass? |
|---|---|---|---|---|
jane.smith@example.com | yes | yes | yes | Yes |
o'brien@example.co.uk | yes | yes | yes | Yes, apostrophes are allowed |
name+news@gmail.com | yes | yes | yes | Yes, plus tags are allowed |
user@sub.example.io | yes | yes | yes | Yes |
plainaddress | no | no | no | No |
name@gmailcom | no | yes | no | No, missing dot |
name@@gmail.com | no | no | no | No |
name @gmail.com | no | no | no | No, contains a space |
.jane@example.com | yes | yes | no | No, starts with a dot |
jane.@example.com | yes | yes | no | No, dot before the @ |
jane..smith@example.com | yes | yes | no | No, two dots in a row |
jane@example..com | yes | no | no | No |
jane@-example.com | yes | no | no | No, domain starts with a hyphen |
user@localhost | no | yes | no | Not for a public signup form |
jane@example.c | yes | yes | no | No, one-letter ending |
jane@exa_mple.com | yes | no | no | No, underscore in domain |
"quoted name"@example.com | no | no | no | Valid in the email standard, but rare |
jürgen@example.de | yes | no | no | Valid only on servers that support international addresses |
What the results show:
- Eleven of the 18 addresses are clearly broken. The short pattern accepted 7 of those 11, mostly addresses with a dot or hyphen in the wrong place. It is fine for a quick hint, but not as your only check.
- The HTML5 pattern accepted 5 of the 11 broken addresses. It allows a local part that starts, ends or repeats a dot, and it allows a domain with no dot, so
name@gmailcomanduser@localhostpass. - The practical pattern rejected all 11 broken addresses and
user@localhost, and accepted all 4 normal ones. - None of the three accepts quoted local parts, and only the short pattern accepts non-ASCII letters. If your users have addresses like
jürgen@example.de, test your own pattern carefully, or skip strict regex checks and confirm the address by email.
Validate email with the HTML5 input
The simplest browser check needs no JavaScript at all:
<input type="email" name="email" required autocomplete="email">
The browser blocks the form and shows a message if the format is wrong. In JavaScript you can ask the input directly with input.checkValidity(), or read input.validity.typeMismatch.
Two things to know. First, the browser uses the HTML standard pattern, so it accepts name@gmailcom, as the table shows. Add a pattern attribute if you want the stricter rule. Second, anyone can bypass browser checks by sending the request directly, so always validate again on the server.
A complete form validation example
This example combines the HTML5 input with the practical pattern, trims spaces, and shows a clear message:
<form id="signup" novalidate>
<label for="email">Email</label>
<input id="email" type="email" name="email" required autocomplete="email">
<p id="email-error" role="alert"></p>
<button type="submit">Sign up</button>
</form>
<script>
const practical = /^(?!\.)(?!.*\.\.)[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(?<!\.)@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}$/;
document.getElementById('signup').addEventListener('submit', (event) => {
const input = document.getElementById('email');
const error = document.getElementById('email-error');
const email = input.value.trim();
if (!email) {
error.textContent = 'Please enter your email address.';
} else if (!practical.test(email)) {
error.textContent = 'Please check your email address. It looks incomplete.';
} else {
error.textContent = '';
return; // format is fine: let the form submit, and verify again on the server
}
event.preventDefault();
});
</script>
Check the domain's MX records in Node.js
On the server, Node.js can look up the domain's mail servers with the built-in dns module. No package is needed.
import { promises as dns } from 'node:dns';
async function domainAcceptsMail(email) {
const domain = email.split('@').pop().toLowerCase();
try {
const records = await dns.resolveMx(domain);
// A "null MX" (RFC 7505) means the domain accepts no mail at all.
const usable = records.filter(r => r.exchange && r.exchange !== '.');
return usable.length > 0;
} catch (err) {
if (err.code === 'ENOTFOUND' || err.code === 'ENODATA') return false;
throw err; // timeouts and server errors: treat as unknown, not invalid
}
}
Our test results:
| Address | Result | Why |
|---|---|---|
jane@gmail.com | true | gmail.com has MX records |
jane@gmial.com | false | The typo domain has no MX records |
jane@example.com | false | example.com publishes a null MX: it accepts no mail |
jane@this-domain-does-not-exist-91827.com | false | The domain does not exist |
One detail: the email standard (RFC 5321) lets a sending server fall back to a domain's A record when there are no MX records. This function treats "no MX" as invalid, which is stricter than the standard but catches typo domains like gmial.com. That domain has a website address record but no mail servers.
When a regex is not enough
A perfect format and a working domain still do not prove the mailbox exists. These addresses pass every check on this page and can still bounce:
- Closed mailboxes.
former.employee@realcompany.comhas a valid format and a real domain, but the mailbox was deleted. - Catch-all domains. Some company servers accept mail for any address during delivery and bounce or discard it later. A simple mailbox check gets "yes" for every address.
- Disposable addresses. Temporary inbox services pass format and MX checks, then expire.
- Secure email gateways. Proofpoint, Mimecast and Barracuda often accept every recipient during the check and filter later.
To confirm the mailbox itself, call an email verification API from your server after the format check passes. It runs the SMTP check and handles catch-all domains. The free email verification API on this hub includes 1,000 credits, and the call takes a few lines:
const res = await fetch('https://api.giggal.ai/v1/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GIGGAL_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
const { data } = await res.json();
// data.status: deliverable, undeliverable or unknown
Want to check a single address by hand first? Paste it into the free email verifier.
Frequently asked questions
What is the best regex for email validation in JavaScript?
For most forms, use the pattern from the HTML standard, which is what browsers use for type="email", and add a check for a dot in the domain. The practical pattern above does this and passed every case in our test. A regex can only check format. It cannot tell you whether the mailbox exists.
Can JavaScript check if an email address really exists?
Not in the browser. Server-side JavaScript can look up the domain's MX records, but confirming a mailbox needs an SMTP check, which most hosting networks block on port 25. An email verification API runs that check for you.
Does input type="email" validate email addresses?
It checks the format in the browser using the HTML standard's pattern. It accepts addresses without a dot in the domain, such as user@localhost, and it can be bypassed, so always validate again on the server.
Should I validate email on the client or the server?
Both. Validate in the browser to give people instant feedback, and validate again on the server, because browser checks can be skipped.
Why does my regex reject valid addresses?
Common causes are rejecting the plus sign, apostrophes or long top-level domains such as .technology. Test your pattern against real customer addresses before you ship it.