June 9, 2026
A Practical Regex Cheatsheet for Everyday Tasks
The small set of regex building blocks — anchors, character classes, quantifiers, groups, flags — behind almost every pattern you will actually write, with the greedy-vs-lazy trap that causes most bugs.

Regular expressions have a reputation for being cryptic, but the truth is most of what you will ever need comes from a small set of building blocks used over and over. This cheatsheet focuses on the patterns that actually show up in real code — validation, extraction, find-and-replace — rather than the deep end of regex features nobody memorizes. Keep it nearby, and reach for the Regex Tester whenever you want to try a pattern against real input before committing it to code.
The building blocks
Every regex is built from the same handful of primitives. Once these are in muscle memory, most patterns read at a glance:
- Literals match themselves. The pattern
catmatches the lettersc,a,tin order. - Character classes match one character from a set.
[a-z]matches any lowercase letter,[0-9]any digit,[aeiou]any vowel. Inside a class,^at the start means negation, so[^0-9]matches anything that isn't a digit. - Shorthand classes are the most-used shortcuts:
\dfor digits,\wfor word characters (letters, digits, underscore),\sfor whitespace. Their uppercase counterparts\D,\W,\Sare the negations. - The dot (
.) matches any character except a newline by default. Escape it as\.when you mean a literal period. - Anchors don't match characters — they match positions.
^matches the start of the string,$matches the end. Use these liberally; an unanchored pattern matches anywhere, which is usually looser than you want.
Try it: Regex Tester
Test a regular expression against a string with live highlighted matches.
Quantifiers
Quantifiers say how many times the previous thing can repeat. There are four you will use constantly:
?— zero or one (optional).*— zero or more.+— one or more.{n,m}— betweennandmtimes, inclusive.{3}means exactly 3;{3,}means 3 or more.
The greedy trap. By default, quantifiers are greedy — they match as much as possible. The pattern ".*" applied to the input "foo" and "bar" will match the entire span in one go, including both quoted strings and everything between them, which is rarely what you want when extracting delimited values. Add a ? after a quantifier to make it lazy: ".*?" matches as little as possible. This distinction is the root of a large fraction of regex bugs, so when a pattern matches too much, laziness is the first thing to reach for.
// Greedy: matches the whole span
'"foo" and "bar"'.match(/".*"/)[0]; // '"foo" and "bar"'
// Lazy: matches the shortest possible
'"foo" and "bar"'.match(/".*?"/)[0]; // '"foo"'
Groups and alternation
Parentheses create groups, and groups do two jobs: they scope quantifiers and alternation, and they capture matched text for later use. (ab)+ matches ab, abab, ababab, and so on — the quantifier applies to the whole group. cat|dog matches either cat or dog, and (cat|dog) food matches either "cat food" or "dog food".
Capture groups are numbered left to right, starting at 1, and you can refer back to them with backreferences. \1 means "whatever the first group matched," which lets you detect repeated words:
// Match a doubled word like "the the"
"the the cat".match(/\b(\w+)\s+\1\b/);
// ["the the", "the"]
When you need grouping without capturing — say, to apply a quantifier to an alternation you don't need to extract — use a non-capturing group: (?:cat|dog). This is faster and keeps your capture numbering clean, which matters once you have several groups in play.
Flags
Flags go after the closing slash (or as a second argument to JavaScript's RegExp constructor) and change how the whole pattern behaves:
g— global; find all matches, not just the first.i— case-insensitive.m— multiline; makes^and$match at the start and end of each line, not just the whole string.s— dotall; lets.match newlines as well.
The m and s flags solve opposite newline problems — m changes where anchors match, s changes what the dot matches — and mixing them up is a common source of "why doesn't this work across multiple lines" confusion.
Try it: Regex Tester
Test a regular expression against a string with live highlighted matches.
Patterns you will actually use
A handful of patterns come up constantly. None are perfect — real-world validation is harder than it looks — but these are reasonable starting points:
// Email (pragmatic, not RFC-perfect)
/^[\w.+-]+@[\w-]+\.[\w.-]+$/
// URL with a scheme
/^https?:\/\/[\w.-]+(:\d+)?(\/[^\s]*)?$/
// US-style phone number: (555) 123-4567 or 555-123-4567
/^\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}$/
// ISO date: 2026-06-09
/^\d{4}-\d{2}-\d{2}$/
A note on validation. Regex is good at detecting shape (does this look like an email?) and bad at verifying meaning (is this a real, deliverable address?). For anything user-facing, prefer a permissive pattern that catches obvious mistakes and lets the rest through, then verify by actually sending a confirmation email or making a test request. Overly strict regex is how you end up rejecting users with perfectly valid addresses your pattern didn't anticipate — so test every pattern against a spread of realistic inputs in the Regex Tester before shipping it.