★ 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.
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,}$/| Part | What 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 |
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 | |
|---|---|
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 | |
|---|---|
plainaddress | ✗ |
@example.com | ✗ |
jane@ | ✗ |
jane..doe@example.com | ✗ |
jane@example | ✗ |
jane@-example.com | ✗ |
jane doe@example.com | ✗ |
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());
}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.
Questions engineers actually ask
Is there a perfect email regex?
Does this accept plus-addressing like name+tag@gmail.com?
Does it support international (Unicode) email addresses?
What is catastrophic backtracking?
More web patterns
- URLhttp(s) URLs with a real domain, optional port, path, query and fragment.^https?:\/\/(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,63}(?::\d{1,5})?(?:[\/?#]\S*)?$
- Domain NameHostnames like example.com or sub.example.co.uk, including punycode.^(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,63}$
- URL SlugLowercase kebab-case slugs like my-first-post.^[a-z0-9]+(?:-[a-z0-9]+)*$
- IPv4 AddressDotted-quad IPv4 with each octet 0–255 and no leading zeros.^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$
- IPv6 AddressFull and compressed (::) IPv6 addresses.^(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?::[0-9A-Fa-f]{1,4}){1,6}|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:))$
- Hex ColorCSS hex colours: #rgb, #rgba, #rrggbb and #rrggbbaa.^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$
- HTML TagFinds opening, closing and self-closing tags in a string. Not a parser.<\/?([A-Za-z][A-Za-z0-9-]*)\b[^<>]*>
Or browse the full regex library.
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.
