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

text
re
, and Go's
text
regexp
that you can adapt them with minor changes. Where a flag matters, I'll call it out.

A 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

text
name@host.tld
. It cannot tell you the mailbox exists, the domain resolves, or the user typed their own address correctly. Keep that line in mind and most regex pain disappears.

With 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.

js
const 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:

js
const 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 (

text
new URL()
in JS,
text
urllib.parse
in Python). Use regex only to gatekeep, then hand the string to a real parser.

3. Hex color

Matches

text
#fff
,
text
#ffffff
, and the eight-digit form with alpha:

js
const 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

text
3,4
branch covers
text
#rgb
and
text
#rgba
shorthand.

4. ISO 8601 date (YYYY-MM-DD)

For the date-only subset that shows up in APIs and CSV exports:

js
const 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

text
2026-02-30
and
text
2025-02-29
(not a leap year). Regex genuinely cannot do calendar math. Parse and round-trip the value to confirm it's real:

js
function 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:

js
const 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

text
g
to replace every occurrence.

Collapse runs of whitespace to a single space:

js
text.replace(/\s+/g, ' ');

Trim leading/trailing whitespace (if your language lacks

text
.trim()
):

js
text.replace(/^\s+|\s+$/g, '');

Normalize line endings to

text
\n
:

js
text.replace(/\r\n?/g, '\n');

Caveat:

text
\s
in JavaScript and most engines matches Unicode whitespace including non-breaking spaces and exotic separators, which is usually what you want. If you specifically want only ASCII spaces and tabs, be explicit with
text
[ \t]
— otherwise you may strip characters you intended to keep.

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

text
MAJOR.MINOR.PATCH
version string:

js
const 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

text
(?:...)
— a non-capturing group — when you only need grouping for alternation or quantifiers and don't want the substring captured. It's slightly faster and keeps your group indices clean. Several patterns above already use it for exactly this reason.

8. Find all matches with their positions

When you need every match (not just the first) plus where each one occurred,

text
matchAll
with the
text
g
flag is the modern, safe way — it avoids the stateful
text
lastIndex
footgun of calling
text
regex.exec
in a loop.

js
const 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:

js
const doubled = /\b(\w+)\s+\1\b/gi; 'the the cat sat'.match(doubled); // ['the the']

text
\1
refers back to the first group, and the
text
i
flag makes it case-insensitive so
text
The the
is also caught. Backreferences are powerful but can be slow on adversarial input — see the warning below.

10. 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

text
^
and
text
$
,
text
123abc
would match.

js
const 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

text
Number()
/
text
parseFloat
and check for
text
NaN
instead.

The caveats that actually matter

Anchors are not optional

Most validation bugs come from forgetting

text
^
and
text
$
.
text
/\d{4}/
is true for
text
abc1234xyz
. If you're validating a whole string, anchor it. (Note:
text
$
in multiline mode matches end-of-line, not end-of-string — use
text
\z
in engines that support it, or avoid the
text
m
flag for whole-string validation.)

Catastrophic backtracking is real

Nested quantifiers like

text
(a+)+$
can take exponential time on a non-matching input. This is a genuine denial-of-service vector — a single crafted request can pin a CPU core. If a pattern combines nested
text
+
/
text
*
with alternation, test it against a long mismatching string before trusting it in production. When in doubt, prefer simpler patterns or a real parser.

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.