Regex cheat sheet — the patterns you'll actually use
Regular expressions have a reputation problem. They look like a cat walked
across a keyboard — ^[\w.+-]+@[\w-]+\.[a-z]{2,}$ — and most
developers respond by copying something from Stack Overflow and praying it
works. Fair. But regex isn't magic; it's a small grammar with maybe a dozen
rules, and once you know them you can read that cat-walking for what it
actually says.
This is the cheat sheet I wish someone had handed me: the building blocks that cover 95% of real-world use, a recipe box of patterns you'll genuinely reuse, and the traps that produce the other 5% of bugs. Bookmark it.
- The ~12 building blocks that cover nearly all real regex
- Character classes, quantifiers, anchors, and groups — with examples
- A copy-paste recipe box of patterns you'll actually use
- The greedy/lazy trap, and when not to reach for regex at all
The mental model
A regex is a pattern that describes a set of strings.
/cat/ matches any string containing "cat" — "cat",
"concatenate", "bobcat". Add anchors and it matches only exactly "cat".
The whole game is composing small building blocks into a pattern that
matches the strings you want and nothing else.
Most characters match themselves — /hello/ matches "hello".
But a dozen characters are metacharacters, and they're the
source of both the power and the confusion:
. \ + * ? [ ^ $ { } ( ) | /
To match one literally, escape it: /\./ matches a real period,
/what\?/ matches "what?". A surprising share of all regex bugs
are just unescaped metacharacters.
Character classes — matching one character
A character class matches one character from a set:
. any single character (except newline)
[aeiou] any one vowel
[a-z] any lowercase letter (a range)
[A-Za-z] any letter, either case
[0-9] any digit
[^0-9] anything EXCEPT a digit (^ inside [] means "not")
\d a digit — same as [0-9]
\w a word char — letters, digits, underscore
\s whitespace — space, tab, newline
\D \W \S the negations — "not a digit", etc.
The [^...] "not" class is quietly one of the most useful — it's
how you say "everything up to the next X" without the greedy-matching
problems you'll see below.
Quantifiers — how many
A quantifier says how many times the previous thing may repeat:
a* zero or more a's
a+ one or more a's
a? zero or one a (optional)
a{3} exactly three a's
a{2,} two or more a's
a{2,4} between two and four a's
Quantifiers are greedy by default — they take as much as
they can. On the string <a> <b>, the pattern
<.*> matches from the first
< to the last >, swallowing
everything between. Add ? to make it lazy
(<.*?>), or better, use a negated class:
<[^>]*> — "anything that isn't >" —
which is faster and does what you meant.
Anchors — where, not what
Anchors match a position, not a character. They're how you pin a pattern to the start, end, or a word edge:
^abc starts with "abc"
abc$ ends with "abc"
^abc$ is exactly "abc"
\bcat\b the word "cat" — not the "cat" inside "concatenate"
With the m (multiline) flag, ^ and
$ match the start and end of each line, not just the
whole string. Forgetting to anchor is the classic reason a "validation"
regex quietly accepts xyz123abc456.
Groups and alternation
Parentheses group things; the pipe means "or":
gr(a|e)y "gray" or "grey"
(gray|grey) same thing, spelled out
(\d{4})-(\d{2}) capture groups — \1 and \2 refer back to them
(?:...) non-capturing group — group without remembering
Capture groups are what make find-and-replace powerful: match
(\d{4})-(\d{2})-(\d{2}) and replace with
$2/$3/$1 to turn 2026-07-31 into
07/31/2026. Use (?:...) when you need grouping
but don't want to number a group you'll never use.
The patterns you'll actually use
Here's the recipe box. Copy freely — these cover the vast majority of day-to-day matching:
# email — practical, not RFC-perfect (full email regex is a rabbit hole)
^[\w.+-]+@[\w-]+\.[a-z]{2,}$
# URL
^https?:\/\/[\w.-]+(?:\/\S*)?$
# hex color (#fff or #a1b2c3)
^#?(?:[0-9a-fA-F]{3}){1,2}$
# date, YYYY-MM-DD
\d{4}-\d{2}-\d{2}
# slug (url-friendly identifier)
^[a-z0-9]+(?:-[a-z0-9]+)*$
# semantic version
\d+\.\d+\.\d+(?:-[\w.]+)?$
# UUID
[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}
# trim whitespace from both ends
^\s+|\s+$
# IPv4 — naive; allows 999.999.999.999, so validate ranges in code
(?:\d{1,3}\.){3}\d{1,3}
And the flags you'll reach for (JavaScript syntax):
/abc/g global — find all matches, not just the first
/abc/i case-insensitive
/abc/m multiline — ^ and $ match each line
/abc/s dotall — . matches newlines too
When not to use regex
Part of knowing regex is knowing its limits:
- Don't parse HTML or XML with regex. They're not regular languages; use a real parser. The famous Stack Overflow answer on this is worth a read.
- Don't fully validate email with regex. The RFC-perfect pattern is hundreds of characters and still wrong in practice. Use the simple check above, then send a confirmation email.
- Beware catastrophic backtracking. Nested quantifiers like
(a+)+can hang for seconds on a long non-matching string. Prefer negated classes and keep quantifiers flat. - Know your flavor. JavaScript, PCRE (PHP/Python), and
grepdiffer — lookbehind support, Unicode handling, and flag names aren't universal. Test in the engine you'll ship.
regex101 is the best sandbox — it explains your pattern token by token and shows every match live. The MDN regex guide is the reference to keep open.
Wrap-up
A dozen building blocks — classes, quantifiers, anchors, groups — cover nearly everything you'll write. Anchor your validations, prefer negated classes over lazy dots, and keep the recipe box a bookmark away. The cat-walking becomes readable surprisingly fast once you know the alphabet.
Regex shows up everywhere you need precision in text — including the commit-lint rules that keep a git history tidy. If your messages could use that kind of discipline, the commit message generator drafts a conventional one in seconds, and the cheat sheet covers the format. And when the strings you're matching travel through a URL, the URL encoding post explains why they look the way they do.