How to validate an email address in JavaScript

Everyone wants the regex. The interesting result is what happens when you run the popular one next to the browser’s own check on the same inputs: they disagree on five out of ten.

Three rules, ten inputs

input                              popular simple browser
"me@example.com"                   true    true   true
"a@b.co"                           true    true   true
"first.last+tag@sub.example.co.uk" true    true   true
"üser@example.com"                 true    true   false
"me@example"                       false   false  true
"me@@example.com"                  false   false  false
"no-at-sign.com"                   false   false  false
"me@localhost"                     false   false  true
"\"quoted string\"@example.com"    true    false  false
"me@[192.168.0.1]"                 true    true   false

popular is the RFC-flavoured regex that gets copied out of the accepted answer, simple is /^[^\s@]+@[^\s@]+\.[^\s@]+$/, and browser is checkValidity() on an <input type="email">.

They agree on the first three and the two obvious rejects. Everything else is a fight.

Where they disagree

"üser@example.com"              regex=true browser=false
"me@example"                    regex=false browser=true
"me@localhost"                  regex=false browser=true
"\"quoted string\"@example.com" regex=true browser=false
"me@[192.168.0.1]"              regex=true browser=false

Note the direction: it goes both ways. The regex is not a stricter browser, and the browser is not a looser regex. They are two different rules that happen to agree on the uninteresting cases.

That matters practically, because it kills the plan people usually have — use type="email" in the form and the same regex on the server. Those two will reject different users.

The browser is not implementing RFC 5322

me@example and me@localhost are accepted: the browser does not require a dot in the domain. üser@example.com is rejected, although addresses like it exist and deliver.

This is not a bug. The HTML specification defines its own e-mail pattern and openly labels it a wilful violation of RFC 5322 — chosen to be implementable and practical rather than complete. So “the browser gets it wrong” is the wrong frame: the browser is following a different, documented rule.

The failure mode that costs you something

real addresses the popular regex rejects:
  me@localhost                 popular=false browser=true
  user@[IPv6:2001:db8::1]      popular=false browser=false

A regex that lets a typo through costs you one bounced mail. A regex that rejects a real address costs you the user, who now cannot sign up and has no idea why. Those two errors are not equally bad, so the pattern should lean loose.

What actually proves anything

Nothing above tells you the address exists, or that the person typing it can read it. Only one thing does: send a mail with a one-time link and see whether someone clicks it.

So the arrangement that works:

  • <input type="email" required> in the form — a free client-side check and, on phones, the right keyboard
  • something loose on the server, /^[^\s@]+@[^\s@]+\.[^\s@]+$/, to catch the missing @
  • a confirmation mail for the actual decision

The regex is a typo-catcher. Treat it as one and it stops being a hard problem.

About Netcup (advertisement)

The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).