Pattern library

Common regex patterns, tested

Copy-ready JavaScript patterns, each checked against the examples shown and run through the ReDoS checker. Read the caveat before you rely on one.

Web & network

Email address

The pattern the HTML standard uses for <input type="email">: what browsers accept as a valid address.

/^[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])?)*$/
Matches
  • jane.doe@example.com (matches)
  • first+tag@sub.example.co.uk (matches)
  • o'brien@example.ie (matches)
  • user@localhost (matches)
Does not match
  • jane.doe@ (does not match)
  • @example.com (does not match)
  • jane doe@example.com (does not match)
  • jane@-example.com (does not match)
  • jane@example..com (does not match)

Caveat: It checks the shape only. It accepts addresses without a dot in the domain (user@localhost) and rejects some that RFC 5322 allows (quoted local parts, non-ASCII addresses). No regex can tell whether a mailbox exists or receives mail.

Web address (http/https URL)

An http or https URL with a dotted host name, an optional port and any path, query and fragment.

/^https?:\/\/(?:[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?\.)+[a-z]{2,63}(?::\d{1,5})?(?:[\/?#]\S*)?$/i
Matches
  • https://example.com (matches)
  • http://example.com:8080/ (matches)
  • https://sub.example.co.uk/path?q=1#top (matches)
  • HTTPS://EXAMPLE.ORG/a/b (matches)
Does not match
  • ftp://example.com (does not match)
  • https://localhost (does not match)
  • example.com (does not match)
  • https://exa mple.com (does not match)
  • https://-bad.example.com (does not match)

Caveat: It does not accept IP addresses, localhost or internationalised host names, and it does not check the path. To really parse or validate a URL in JavaScript, use new URL(text) in a try/catch.

Domain name

A fully qualified host name: labels of up to 63 letters, digits and inner hyphens, a letters-only top-level domain, 253 characters at most.

/^(?=.{1,253}$)(?:[a-z\d](?:[a-z\d-]{0,61}[a-z\d])?\.)+[a-z]{2,63}$/i
Matches
  • example.com (matches)
  • sub.example.co.uk (matches)
  • xn--bcher-kva.example (matches)
  • a-b.c-d.io (matches)
Does not match
  • example (does not match)
  • -example.com (does not match)
  • example-.com (does not match)
  • exa_mple.com (does not match)
  • example.c0m (does not match)

Caveat: Internationalised names must be in their xn-- (Punycode) form. Matching the format says nothing about whether the domain is registered.

IPv4 address

Four decimal numbers from 0 to 255 separated by dots, without 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)$/
Matches
  • 192.168.0.1 (matches)
  • 0.0.0.0 (matches)
  • 255.255.255.255 (matches)
  • 10.20.30.40 (matches)
Does not match
  • 256.1.1.1 (does not match)
  • 192.168.0 (does not match)
  • 192.168.0.1.5 (does not match)
  • 01.2.3.4 (does not match)
  • 1.2.3.4 (does not match)

Caveat: Leading zeros (010.0.0.1) are rejected on purpose because some software reads them as octal. CIDR suffixes such as /24 are not included.

IPv6 address

Every form RFC 3986 allows: full, compressed with ::, and with an embedded IPv4 address.

/^(?:(?:[\da-f]{1,4}:){6}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?: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))|::(?:[\da-f]{1,4}:){5}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?: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))|(?:[\da-f]{1,4})?::(?:[\da-f]{1,4}:){4}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?: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))|(?:(?:[\da-f]{1,4}:){0,1}[\da-f]{1,4})?::(?:[\da-f]{1,4}:){3}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?: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))|(?:(?:[\da-f]{1,4}:){0,2}[\da-f]{1,4})?::(?:[\da-f]{1,4}:){2}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?: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))|(?:(?:[\da-f]{1,4}:){0,3}[\da-f]{1,4})?::[\da-f]{1,4}:(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?: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))|(?:(?:[\da-f]{1,4}:){0,4}[\da-f]{1,4})?::(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?: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))|(?:(?:[\da-f]{1,4}:){0,5}[\da-f]{1,4})?::[\da-f]{1,4}|(?:(?:[\da-f]{1,4}:){0,6}[\da-f]{1,4})?::)$/i
Matches
  • 2001:db8::1 (matches)
  • ::1 (matches)
  • :: (matches)
  • fe80::a1b2:c3d4:e5f6:789 (matches)
  • 2001:0db8:0000:0000:0000:ff00:0042:8329 (matches)
  • ::ffff:192.0.2.128 (matches)
Does not match
  • 2001:db8:::1 (does not match)
  • 1:2:3:4:5:6:7:8:9 (does not match)
  • 2001:db8::g (does not match)
  • 12345:: (does not match)
  • ::ffff:192.0.2.256 (does not match)

Caveat: Zone indexes (fe80::1%eth0) and CIDR prefixes are not accepted. It is long because it lists each place where :: may appear; that keeps it exact without backtracking risk.

MAC address

Six pairs of hex digits separated by colons or hyphens, using the same separator throughout.

/^[\da-f]{2}([:-])(?:[\da-f]{2}\1){4}[\da-f]{2}$/i
Matches
  • 00:1a:2b:3c:4d:5e (matches)
  • 00-1A-2B-3C-4D-5E (matches)
  • ff:ff:ff:ff:ff:ff (matches)
Does not match
  • 00:1a-2b:3c:4d:5e (does not match)
  • 00:1a:2b:3c:4d (does not match)
  • 001a2b3c4d5e (does not match)
  • 00:1g:2b:3c:4d:5e (does not match)

Caveat: The backreference \1 makes the separators consistent. Cisco-style dotted notation (0123.4567.89ab) is not included.

Dates & times

ISO 8601 date

A calendar date written YYYY-MM-DD with a month from 01 to 12 and a day from 01 to 31.

/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/
Matches
  • 2024-01-31 (matches)
  • 1999-12-01 (matches)
  • 2024-02-29 (matches)
Does not match
  • 2024-13-01 (does not match)
  • 2024-00-10 (does not match)
  • 2024-1-5 (does not match)
  • 24-01-31 (does not match)
  • 2024/01/31 (does not match)

Caveat: It cannot know month lengths or leap years, so 2023-02-30 passes. Parse the date and compare the parts afterwards if that matters.

ISO 8601 / RFC 3339 date-time

An internet timestamp: date, T, time with seconds, optional fractions and a Z or ±hh:mm offset (what toISOString() produces).

/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d:(?:[0-5]\d|60)(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/
Matches
  • 2024-05-06T07:08:09Z (matches)
  • 2024-05-06T07:08:09.123Z (matches)
  • 2024-05-06T23:59:60+01:00 (matches)
  • 1985-04-12T23:20:50.52-04:00 (matches)
Does not match
  • 2024-05-06 07:08:09Z (does not match)
  • 2024-05-06T24:00:00Z (does not match)
  • 2024-05-06T07:08Z (does not match)
  • 2024-05-06T07:08:09 (does not match)

Caveat: Second 60 is allowed for leap seconds. Like the date pattern, it does not check month lengths. RFC 3339 also permits a lowercase t and z and a space separator; this pattern does not.

Time, 24-hour

HH:MM with optional :SS, from 00:00 to 23:59:59.

/^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/
Matches
  • 00:00 (matches)
  • 09:30 (matches)
  • 23:59:59 (matches)
  • 12:05:00 (matches)
Does not match
  • 24:00 (does not match)
  • 9:30 (does not match)
  • 12:60 (does not match)
  • 12:30:5 (does not match)
  • 12.30 (does not match)

Caveat: Hours need two digits (09:30, not 9:30) and 24:00 is not accepted.

Time, 12-hour with AM/PM

H:MM or HH:MM from 1:00 to 12:59 followed by AM or PM, with or without a space.

/^(?:0?[1-9]|1[0-2]):[0-5]\d ?[ap]m$/i
Matches
  • 9:30 AM (matches)
  • 12:00pm (matches)
  • 07:15 PM (matches)
  • 1:05am (matches)
Does not match
  • 13:00 PM (does not match)
  • 0:30 AM (does not match)
  • 9:30 (does not match)
  • 9:30 XM (does not match)
  • 9:5 AM (does not match)

Caveat: It does not accept a.m./p.m. with dots or 0:30.

Identifiers & codes

UUID

An RFC 9562 UUID of version 1 to 8 with the standard variant, such as the output of crypto.randomUUID().

/^[\da-f]{8}-[\da-f]{4}-[1-8][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/i
Matches
  • 123e4567-e89b-12d3-a456-426614174000 (matches)
  • 550e8400-e29b-41d4-a716-446655440000 (matches)
  • 0190F3A2-7B3C-7DEF-8ABC-0123456789AB (matches)
Does not match
  • 00000000-0000-0000-0000-000000000000 (does not match)
  • 123e4567e89b12d3a456426614174000 (does not match)
  • 123e4567-e89b-12d3-c456-426614174000 (does not match)
  • 123e4567-e89b-92d3-a456-42661417400 (does not match)

Caveat: The nil UUID (all zeros) and the max UUID (all f) are valid but rejected here because their version digit is 0 or f. Drop the version and variant checks to accept any 8-4-4-4-12 hex string.

Semantic version

The pattern published by semver.org (with JavaScript named groups): major.minor.patch, an optional pre-release and build metadata.

/^(?<major>0|[1-9]\d*)\.(?<minor>0|[1-9]\d*)\.(?<patch>0|[1-9]\d*)(?:-(?<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
Matches
  • 1.0.0 (matches)
  • 0.1.2-alpha.1 (matches)
  • 10.20.30+build.5 (matches)
  • 1.0.0-rc.1+20240506 (matches)
Does not match
  • 1.0 (does not match)
  • 01.0.0 (does not match)
  • v1.2.3 (does not match)
  • 1.2.3- (does not match)
  • 1.2.3-01 (does not match)

Caveat: A leading v (v1.2.3) is not part of SemVer, so it is rejected; strip it first if your tags use it.

URL slug

Lowercase letters and digits in words joined by single hyphens, like my-first-post.

/^[a-z\d]+(?:-[a-z\d]+)*$/
Matches
  • hello-world (matches)
  • post-2024 (matches)
  • a (matches)
  • regex-cheat-sheet (matches)
Does not match
  • Hello-World (does not match)
  • -leading (does not match)
  • trailing- (does not match)
  • double--hyphen (does not match)
  • under_score (does not match)

Caveat: ASCII only; transliterate accented letters before checking (é → e).

Hex colour

A CSS hex colour: #rgb, #rgba, #rrggbb or #rrggbbaa.

/^#(?:[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i
Matches
  • #fff (matches)
  • #C8381F (matches)
  • #c8381f80 (matches)
  • #0f08 (matches)
Does not match
  • fff (does not match)
  • #ff (does not match)
  • #fffff (does not match)
  • #ggg (does not match)
  • #c8381f8 (does not match)

Caveat: Five- and seven-digit forms are rejected because CSS does not allow them. Named colours and rgb() are out of scope.

Base64 string

Standard Base64 with correct = padding (length a multiple of 4).

/^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$/
Matches
  • TWFu (matches)
  • TWE= (matches)
  • TQ== (matches)
  • SGVsbG8sIHdvcmxkIQ== (matches)
Does not match
  • TWE (does not match)
  • TQ= (does not match)
  • T=== (does not match)
  • SGVsbG8@ (does not match)
  • TWFu= (does not match)

Caveat: It also matches the empty string, which is valid Base64 for zero bytes. The URL-safe alphabet (- and _) and unpadded data are not accepted.

Phone & postal

Phone number (E.164)

The international format phone systems use: a plus sign, a country code and up to 15 digits in total, no spaces.

/^\+[1-9]\d{1,14}$/
Matches
  • +14155552671 (matches)
  • +442071838750 (matches)
  • +819012345678 (matches)
Does not match
  • 14155552671 (does not match)
  • +0123456789 (does not match)
  • +1 415 555 2671 (does not match)
  • +1234567890123456 (does not match)

Caveat: It checks length and characters, not whether the country code or number exists. Normalise user input (strip spaces, dashes and brackets) before testing.

US ZIP code

Five digits, optionally followed by a hyphen and the four-digit ZIP+4 extension.

/^\d{5}(?:-\d{4})?$/
Matches
  • 12345 (matches)
  • 12345-6789 (matches)
  • 00501 (matches)
Does not match
  • 1234 (does not match)
  • 123456 (does not match)
  • 12345-678 (does not match)
  • 12345 6789 (does not match)

Caveat: Format only: not every five-digit number is an assigned ZIP code.

UK postcode

The outward code (area and district) and inward code of a UK postcode, with or without the space, plus the special GIR 0AA.

/^(?:[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0AA)$/i
Matches
  • SW1A 1AA (matches)
  • M1 1AE (matches)
  • B33 8TH (matches)
  • CR2 6XH (matches)
  • DN55 1PT (matches)
  • EC1A1BB (matches)
  • GIR 0AA (matches)
Does not match
  • SW1A 1A (does not match)
  • 12345 (does not match)
  • SW1A-1AA (does not match)
  • W1A 0A1 (does not match)
  • 1AA 1AA (does not match)

Caveat: It follows the general format but not every rule about which letters may appear in each position, so a few impossible postcodes pass. Use an address lookup if you need real postcodes.

Text & numbers

Decimal number

An integer or decimal with an optional sign and exponent: 42, -3.14, .5, 6.02e23.

/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i
Matches
  • 42 (matches)
  • -3.14 (matches)
  • .5 (matches)
  • 6.02e23 (matches)
  • +1E-9 (matches)
  • 10. (matches)
Does not match
  • (empty) (does not match)
  • . (does not match)
  • 1,000 (does not match)
  • 1.2.3 (does not match)
  • e5 (does not match)
  • --1 (does not match)

Caveat: No thousands separators and only a dot as the decimal mark. It is written so that each digit can only be matched one way (\d+\.?\d* would backtrack quadratically on long digit runs).

Letters in any script

One or more letters from any writing system, including combining accents (é written as e + ◌́).

/^[\p{L}\p{M}]+$/u
Matches
  • hello (matches)
  • Zoë (matches)
  • Ελληνικά (matches)
  • 日本語 (matches)
  • मराठी (matches)
Does not match
  • hello world (does not match)
  • R2D2 (does not match)
  • O'Neil (does not match)
  • café! (does not match)

Caveat: Needs the u flag. Spaces, apostrophes and hyphens are rejected, so it is not a name validator: real names can contain all of those.

Emoji (including sequences)

Every emoji the Unicode standard recommends, matched as whole sequences: flags, skin tones and joined emoji count as one match.

/\p{RGI_Emoji}/gv
In “Ship it 🚀 — thanks 👍🏽! Team 👩‍💻 in 🇬🇧” it finds
  • 🚀 (matches)
  • 👍🏽 (matches)
  • 👩‍💻 (matches)
  • 🇬🇧 (matches)

Caveat: Needs the v flag (Chrome 112, Firefox 116, Safari 17, Node.js 20 or later). Text-style symbols without emoji presentation, such as a plain ©, are not matched.

Repeated words

Finds a word typed twice in a row (“the the”), using a backreference to the first word.

/\b(\w+)\s+\1\b/gi
In “This is is a test. The the cat sat on the mat mat.” it finds
  • is is (matches)
  • The the (matches)
  • mat mat (matches)

Caveat: \w and \b only know ASCII letters, so repeated words with accents are missed. Legitimate repeats (“had had”) are found too.

Thousands separators

Matches the empty positions where a comma belongs in a long number; replace them with “,”.

/\B(?=(?:\d{3})+(?!\d))/g
Replacing with “,” turns
  • Population: 1234567, budget 9876543210
into
  • Population: 1,234,567, budget 9,876,543,210

Caveat: Works on runs of digits without a decimal part. For user-facing numbers, Intl.NumberFormat handles locales and decimals properly.

Password with mixed characters

At least 12 characters including a lowercase letter, an uppercase letter, a digit and a symbol, each checked by its own lookahead.

/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{12,}$/
Matches
  • Correct-Horse-9 (matches)
  • aB3$aB3$aB3$ (matches)
  • Tr0ub4dor&3xyz (matches)
Does not match
  • short1A! (does not match)
  • alllowercase123! (does not match)
  • ALLUPPERCASE123! (does not match)
  • NoDigitsHere!! (does not match)
  • NoSymbols12345 (does not match)

Caveat: Composition rules like these are no longer recommended on their own: NIST guidance favours length and checking against breached-password lists. Use this as an example of lookaheads.

Using these patterns

Every validation pattern here is anchored with ^ and $, so it checks a whole value such as a form field. To find the same thing inside longer text, remove the anchors and add the g flag, or use \b where it makes sense. Open in tester loads the pattern with all its examples, one per line, with the m flag added so the anchors work line by line.

Regexes are best at format. A pattern can confirm that a date is written YYYY-MM-DD, not that the day exists; that an address has the shape of an email, not that someone reads it. Pair them with real parsing (new URL(), Date, a postcode lookup) when correctness matters, and keep length limits on untrusted input.

Each card’s Try your own value box tests input against the pattern as you type, entirely in your browser.

Questions

Do these patterns work outside JavaScript?

Most use syntax shared by every major flavour. Named groups, \p{…} and the v flag are the exceptions; the tester’s Code tab converts a pattern for Python, PHP, Go and Java and lists what differs.

Why not the “complete” RFC 5322 email regex?

It is thousands of characters long, accepts addresses no mail provider hands out, and still cannot tell whether a mailbox exists. The browser’s own rule is a better balance; send a confirmation email to be sure.

Can I use them in commercial projects?

Yes. Short patterns like these are standard building blocks; the semantic version pattern is the one published on semver.org.