Dev Hub Solutions

Product studio

Get in touch
8 min readregex / reference / fundamentals

The 12 regex patterns you'll actually use

Email, URL, phone, IP, UUID, dates, passwords, JWT — the patterns that show up in code review again and again. With the edge cases each one gets wrong.

Regex is one of those skills where you don't need to know everything — you need to know the dozen patterns that show up in real code, and the specific failure modes each one has. Here they are.

All patterns use JavaScript regex syntax. If you're writing for Python's re, Go's regexp, or Ruby's Oniguruma, behaviour mostly matches with a few documented exceptions.

1. Email (practical)

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

What it catches: at least one non-whitespace, non-@ character, then @, then more, then ., then a TLD.

What it misses: actual RFC 5322 grammar (which permits quoted local parts, comments, IP-address domains). What it accepts that breaks downstream: a@b.c is technically valid here but most mail systems reject it.

Use for: form input validation where you'll send a confirmation email anyway. The confirmation is the real check.

Open in tester.

2. URL (looks-like)

/^https?:\/\/[^\s]+$/

What it catches: starts with http:// or https://, then non-whitespace.

What it misses: real URL validation. https:////// matches. http://!! matches.

Use for: prose extraction (text.match(/https?:\/\/[^\s]+/g)). For real validation, use new URL(input) and catch the throw.

Open in tester.

3. US phone number

/^\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/

What it catches: (555) 123-4567, 555-123-4567, 555.123.4567, 555 123 4567, 5551234567.

What it misses: international formats. +44 20 7946 0958 doesn't match. For multi-country input, use libphonenumber-js — Google's library is the canonical source.

Open in tester.

4. IPv4 (shape only)

/^(?:\d{1,3}\.){3}\d{1,3}$/

What it catches: four decimal numbers, 1-3 digits, separated by dots.

What it misses: that each octet must be 0-255. 999.999.999.999 matches.

Use for: pre-filter before validating ranges in code. Or use a real IP-validation library.

Open in tester.

5. UUID v4 (strict)

/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i

Validates the 8-4-4-4-12 hex layout, the version nibble 4 in position 14, and the variant nibble (8, 9, a, or b) in position 19.

Strict to v4. For any UUID version, replace 4[0-9a-f]{3} with [1-7][0-9a-f]{3} and [89ab] with [0-9a-f].

Open in tester.

6. ISO 8601 date

/^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?$/

Date-only (2026-05-15) or full datetime (2026-05-15T14:30:00Z, 2026-05-15T14:30:00.123+05:30).

What it misses: that the date is real. 2026-02-30 matches. For real validation, parse with new Date(input) and check the ISO round-trip.

Open in tester.

7. Strong password rule

/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9])[A-Za-z\d^A-Za-z0-9]{12,}$/

At least 12 chars, with at least one lowercase, uppercase, digit, and symbol.

Worth noting: this kind of rule is widely considered weaker than pure length. NIST SP 800-63B (2024 update) recommends minimum length over composition rules. Use this only when a system imposes class requirements you can't change. Our password generator generates passwords that satisfy this without falling into predictable patterns.

Open in tester.

8. JWT (three Base64url segments)

/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/

Three Base64url segments joined by dots. Note: the signature segment can be empty (for alg: none tokens, which is itself a finding — see our JWT decoding post).

9. Semantic version

/^\d+\.\d+\.\d+(?:-[\w.]+)?(?:\+[\w.]+)?$/

Matches 1.2.3, 1.2.3-rc.1, 1.2.3+build.42, 1.2.3-rc.1+build.42.

Misses some edge cases of the full SemVer 2.0.0 grammar (pre-release identifiers must not have leading zeros except 0 itself). Close enough for 99% of validation.

10. Hexadecimal color

/^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i

Matches #FFF, #FFFA (4-digit with alpha), #FFFFFF, #FFFFFFAA (8-digit with alpha). Case-insensitive flag accepts both #FFF and #fff.

If you also need to convert HEX to other formats, see /color-picker/hex-to-rgb.

11. Whitespace-trim source

/^\s+|\s+$/g

Strips leading and trailing whitespace. str.replace(/^\s+|\s+$/g, '') is the regex equivalent of str.trim() — but .trim() is faster and clearer; only use the regex if you need a non-standard whitespace definition.

12. Markdown link extraction

/\[([^\]]+)\]\(([^)]+)\)/g

Captures [text](url) pairs from Markdown. Group 1 is the link text, group 2 is the URL.

What it misses: nested brackets inside link text ([outer [inner] link](url)), URLs containing parentheses. For full Markdown link parsing, use a real Markdown parser — but for "find all the obvious links in a doc", this works fine.

Common mistakes that look like regex bugs

Greedy when you wanted lazy. <.*> matches <a>foo</a> in full because * is greedy. <.*?> matches just <a>. Append ? to any quantifier to make it lazy.

Forgot the g flag. str.match(/foo/) returns the first match; str.match(/foo/g) returns all. The default trips up newcomers regularly.

Anchors silently missing. /\d+/.test("abc123def") returns true — the regex matches the 123 in the middle. If you wanted "entire string is digits", you need ^\d+$.

Escape oversights inside character classes. [.] is a literal dot. [\.] is also a literal dot. [.-] is a literal dot OR hyphen at the end of a class. But [a-z] is a range. The - is special only between characters.

Where to test

Our regex tester does live match highlighting, capture groups, all the flags. The pre-loaded pattern pages cover each of the patterns above with a test corpus and "open in tester" link so you can iterate.