New blogMaking an AI Agent You Can Trust With Money

★ Web · regex

Regex for Email Address

A practical email pattern for sign-up forms. It accepts dots, plus-addressing and subdomains, rejects consecutive dots and missing TLDs, and stays far simpler than the full RFC 5322 grammar, which almost nobody needs.

  • Runs in your browser
  • No signup

Try the email address pattern

Read it in plain English (best-effort)

  • ^start of a line
  • [A-Za-z0-9_%+-]one character from: A–Z, a–z, 0–9, '_', '%', '+', …
  • +one or more of the previous
  • (?:start a group (not captured)
  • .the character '.'
  • [A-Za-z0-9_%+-]one character from: A–Z, a–z, 0–9, '_', '%', '+', …
  • +one or more of the previous
  • )end of group
  • *zero or more of the previous
  • @the character '@'
  • [A-Za-z0-9]one character from: A–Z, a–z, 0–9
  • (?:start a group (not captured)
  • [A-Za-z0-9-]one character from: A–Z, a–z, 0–9, '-'
  • {0,61}between 0 and 61 of the previous
  • [A-Za-z0-9]one character from: A–Z, a–z, 0–9
  • )end of group
  • ?the previous is optional
  • (?:start a group (not captured)
  • .the character '.'
  • [A-Za-z0-9]one character from: A–Z, a–z, 0–9
  • (?:start a group (not captured)
  • [A-Za-z0-9-]one character from: A–Z, a–z, 0–9, '-'
  • {0,61}between 0 and 61 of the previous
  • [A-Za-z0-9]one character from: A–Z, a–z, 0–9
  • )end of group
  • ?the previous is optional
  • )end of group
  • *zero or more of the previous
  • .the character '.'
  • [A-Za-z]one character from: A–Z, a–z
  • {2,}2 or more of the previous
  • $end of a line

One sample per line works well with the m flag on anchored patterns.

Waiting for a pattern
01

The pattern, piece by piece

/^[A-Za-z0-9_%+-]+(?:\.[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])?)*\.[A-Za-z]{2,}$/
Pattern explanation
PartWhat it does
^start of the string
[A-Za-z0-9_%+-]+local part: letters, digits, _ % + -
(?:\.[A-Za-z0-9_%+-]+)*optional dot-separated parts, so no leading, trailing or double dots
@the at sign
[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?a domain label that doesn't start or end with a hyphen
(?:\.label)*any number of subdomains
\.[A-Za-z]{2,}a dot and a top-level domain of 2+ letters
$end of the string
02

What it accepts and rejects

Every sample below is checked automatically in JavaScript and Python. The tester above is preloaded with all of them, one per line.

Should match
Should match
jane@example.com✓
john.doe+news@sub.example.co.uk✓
a_b-c@my-domain.io✓
x@y.ai✓
first.last@company.travel✓
user%tag@example.org✓
Should not match
Should not match
plainaddress✗
@example.com✗
jane@✗
jane..doe@example.com✗
jane@example✗
jane@-example.com✗
jane doe@example.com✗
03

Copy it into your code

RE2-safeNo lookarounds or backreferences, so the same pattern works in Go's RE2 engine and in Rust's regex crate.

const emailRe = /^[A-Za-z0-9_%+-]+(?:\.[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])?)*\.[A-Za-z]{2,}$/;

export function isValidEmail(value) {
  return emailRe.test(value.trim());
}
04

When not to use regex for this

Regex can only tell you an address looks plausible. Whether it exists is decided by sending a confirmation link. Keep the regex loose, then verify by email. Rejecting a real customer's unusual address costs more than letting a typo through to a bounce.

05

Questions engineers actually ask

Is there a perfect email regex?
No. The RFC grammar allows quoted local parts, comments and IP literals that real inboxes almost never use. A strict pattern rejects valid edge cases; a loose one plus a confirmation email is the standard approach.
Does this accept plus-addressing like name+tag@gmail.com?
Yes. The + is allowed in the local part, so filtered addresses work.
Does it support international (Unicode) email addresses?
No, it is ASCII-only. If you need internationalised addresses, relax the character classes or validate with your email provider instead.
What is catastrophic backtracking?
Some patterns, usually nested quantifiers like (a+)+, make a backtracking engine try an exponential number of paths on input that almost matches. The tester stops any run after 300ms, so a bad pattern can't freeze your tab. In production, that same pattern can take down a server (a ReDoS attack).
06

More web patterns

07

More from the workbench

Fine print

Tools: on the house.
Engineer: by the hour.

The regex was free. Code that doesn't need one is the upgrade: typed schemas, validated once at the edge, trusted everywhere after.

I'm Himanshu, an AI and full-stack engineer. I build MVPs, AI agents and the growth plumbing around them for early-stage startups.