Regex is one of those tools you reach for every few weeks, forget the exact syntax for, and end up re-deriving from a Stack Overflow answer you only half trust. This is a short, opinionated set of patterns worth keeping somewhere you can paste them from, along with the caveats that keep them from biting you later.
All examples use JavaScript-flavored syntax (ECMAScript), which is close enough to PCRE, Python's
reregexpA word before the patterns
Regex is excellent at one job: recognizing whether a string matches a shape. It is bad at the job people most often ask of it: deciding whether a string is semantically valid. An email regex can confirm something looks like
name@host.tldWith that out of the way:
1. Email-ish (deliberately loose)
The full RFC 5322 grammar for email addresses is famously horrifying and matches things no real mail server accepts. Don't use it. Use a pragmatic pattern that catches obvious typos and defers real validation to a confirmation email.
jsconst email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; email.test('dev@example.com'); // true email.test('not-an-email'); // false email.test('a@b'); // false (no TLD)
Caveat: This rejects technically-valid-but-rare addresses (no-dot domains, quoted local parts) and accepts plenty of unroutable ones. That's the correct trade-off for a signup form. The only authoritative check is sending mail and seeing if it arrives.
2. URL (http/https)
A URL pattern that's strict enough to reject garbage but loose enough to accept real-world links:
jsconst url = /^https?:\/\/[^\s/$.?#].[^\s]*$/i; url.test('https://cosmovexapps.com/tools/regex-tester'); // true url.test('http://localhost:3000/path?q=1'); // true url.test('justtext'); // false
Caveat: If you actually need to parse a URL — pull out the host, port, query params — don't use regex. Every modern language ships a URL parser (
new URL()urllib.parse3. Hex color
Matches
#fff#ffffffjsconst hex = /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/; hex.test('#fff'); // true (shorthand) hex.test('#ff8800'); // true hex.test('#ff8800cc'); // true (with alpha) hex.test('#ggg'); // false
The alternation order matters only for readability here; each branch is anchored by length. The
3,4#rgb#rgba4. ISO 8601 date (YYYY-MM-DD)
For the date-only subset that shows up in APIs and CSV exports:
jsconst isoDate = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/; isoDate.test('2026-06-02'); // true isoDate.test('2026-13-02'); // false (no month 13) isoDate.test('2026-06-32'); // false (no day 32)
Caveat: This validates shape, not calendar reality. It happily accepts
2026-02-302025-02-29jsfunction isRealDate(s) { const d = new Date(s + 'T00:00:00Z'); return !isNaN(d) && d.toISOString().slice(0, 10) === s; }
5. Slug
Lowercase letters, digits, and single hyphens — no leading, trailing, or doubled hyphens:
jsconst slug = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; slug.test('regex-patterns-to-bookmark'); // true slug.test('Regex-Patterns'); // false (uppercase) slug.test('-leading-hyphen'); // false slug.test('double--hyphen'); // false
This is the validator. Generating a clean slug from arbitrary text is a different job — you lowercase, strip accents, replace runs of non-alphanumerics with single hyphens, then trim. That's more reliably done in code than in one regex, and if you just need one quickly there's a free slug generator that handles the edge cases.
6. Whitespace cleanup
Three patterns you'll use constantly. All need the global flag
gCollapse runs of whitespace to a single space:
jstext.replace(/\s+/g, ' ');
Trim leading/trailing whitespace (if your language lacks
.trim()jstext.replace(/^\s+|\s+$/g, '');
Normalize line endings to
\njstext.replace(/\r\n?/g, '\n');
Caveat:
\s[ \t]7. Capture groups: pull structured data out
Capture groups — the parts in parentheses — turn a match into extracted fields. Named groups make the result readable. Here we split a
MAJOR.MINOR.PATCHjsconst semver = /^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$/; const m = '2.14.3'.match(semver); m.groups.major; // '2' m.groups.minor; // '14' m.groups.patch; // '3'
Use
(?:...)8. Find all matches with their positions
When you need every match (not just the first) plus where each one occurred,
matchAllglastIndexregex.execjsconst tagPattern = /#(\w+)/g; const input = 'shipping #regex and #tooling today'; for (const m of input.matchAll(tagPattern)) { console.log(m[1], 'at index', m.index); } // regex at index 9 // tooling at index 20
9. Backreferences: catch duplicated words
A backreference matches the same text a capture group already matched. A classic use is flagging accidentally repeated words in prose — the kind your eye skips over:
jsconst doubled = /\b(\w+)\s+\1\b/gi; 'the the cat sat'.match(doubled); // ['the the']
\1iThe the10. Anchored, trimmed input number
Validating that a string is a plain integer or decimal, optionally signed, is deceptively easy to get wrong. Anchors matter: without
^$123abcjsconst number = /^-?\d+(?:\.\d+)?$/; number.test('42'); // true number.test('-3.14'); // true number.test('1.'); // false (no trailing dot) number.test('.5'); // false (require leading digit) number.test('1e3'); // false (no exponent here)
Decide deliberately whether you accept leading zeros, exponents, or thousands separators, and write the pattern to match that decision. For anything beyond simple cases, parse with
Number()parseFloatNaNThe caveats that actually matter
Anchors are not optional
Most validation bugs come from forgetting
^$/\d{4}/abc1234xyz$\zmCatastrophic backtracking is real
Nested quantifiers like
(a+)+$+*Regex doesn't understand nesting
HTML, JSON, and most config formats are not regular languages — they have arbitrarily nested structure that regex provably cannot match. Use a parser. Regex is fine for yanking one attribute out of known-shaped markup as a one-off, but the moment the input can nest, you're building a bug.
"Looks valid" is not "is valid"
This is the theme of the whole list. Emails need confirmation. Dates need calendar logic. URLs need a parser to resolve. Phone numbers need a country-aware library. Regex is the cheap first gate, never the source of truth.
How to actually work with these
Write the pattern, then immediately throw your nastiest inputs at it — empty strings, leading/trailing junk, Unicode, the value you're sure should fail. Build the test cases before you trust the regex, not after it breaks in production.
Iterating in a tight loop beats squinting at escaped backslashes. A live regex tester that highlights matches and capture groups as you type turns the usual guess-and-check grind into something fast and honest — and like every Cosmovex tool it runs entirely in your browser, so the strings you're testing never leave your machine.
Bookmark the ten patterns, internalize the four caveats, and you'll spend a lot less time fighting your own regular expressions.