Regex for email validation

/^[^\s@]+@[^\s@]+\.[^\s@]+$/

/^[^\s@]+@[^\s@]+\.[^\s@]+$/ checks that an address has the shape of one: some characters that aren’t spaces or @, a single @, then a domain with at least one dot in it. That catches the mistakes people really make, such as a missing @, a stray space or a doubled @, and it accepts every address a real customer has.

What it accepts

The tester below checks one address per line, so it runs with g and m; to test a single value, drop both flags.

[email protected]                      matches
[email protected]   matches
user@localhost                       no: there's no dot after the @
no-at-sign.example.com               no
two@@example.com                     no
spaces [email protected]                no
[email protected]                    matches, but can't be delivered

Why not a complete RFC 5322 regex

RFC 5322 allows quoted local parts with spaces in them ("john doe"@example.com), comments in parentheses, and IP addresses as domains (user@[192.0.2.1]). A regex that follows the whole grammar exists: the one behind Perl’s Mail::RFC822::Address is more than 6,000 characters long. It still proves only that an address is well formed, not that a mailbox is behind it, and nobody can read it or fix it. Most mail providers refuse the unusual forms anyway.

The one check that proves an address works is a message sent to it with a link to confirm. Keep the regex loose, so it never turns away a real person, and let the confirmation email do the rest.

A stricter option

Browsers check <input type="email"> with a pattern from the HTML standard:

/^[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])?)*$/

It rejects [email protected] and keeps each part of the domain to 63 characters. It accepts user@localhost, because a domain without a dot is fine on a company network, and it rejects josé@example.com, which internationalized email (RFC 6531) allows.

In code

const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
EMAIL.test(input.trim()); // true or false

Leave off the g flag. A /g regex remembers lastIndex between calls, so checking the same address twice returns true and then false.

Try it in the Regex Tester

Flags

3 matches

Matches

#MatchAtGroups
1[email protected]0–15none
2[email protected]16–50none
3[email protected]128–145none

What the pattern means

  • ^The start of a line
  • [^\s@]+Any one character except whitespace or "@", one or more times, as many times as possible (greedy)
    • \sA whitespace character (space, tab, line break or another Unicode space)
    • @The character "@"
  • @The character "@"
  • [^\s@]+Any one character except whitespace or "@", one or more times, as many times as possible (greedy)
    • \sA whitespace character (space, tab, line break or another Unicode space)
    • @The character "@"
  • \.The character "." itself: escaped, so it isn't "any character"
  • [^\s@]+Any one character except whitespace or "@", one or more times, as many times as possible (greedy)
    • \sA whitespace character (space, tab, line break or another Unicode space)
    • @The character "@"
  • $The end of a line
  • g global: Find every match, not just the first. exec() and test() carry on from lastIndex.
  • m multiline: ^ and $ match at the start and end of each line, not just of the whole string.

Runs in your browser. Nothing you enter is sent anywhere.