Regex Cheat Sheet
The regex syntax developers actually reach for — anchors, character classes, groups, and lookaround — with real patterns, common mistakes, and a searchable quick reference.
Anchors & Boundaries
Anchors don't match a character — they match a position. That distinction is why they can sit right next to a quantifier without ever being 'repeated.'
^Matches the very start of the string. With the m flag, matches the start of every line instead.
/^Hello/.test("Hello world") // true /^world/.test("Hello world") // false
$Matches the very end of the string. With the m flag, matches the end of every line instead.
\bMatches a word boundary — the position between a word character (\w) and a non-word character, or the start/end of the string. Doesn't consume any character itself.
/\bcat\b/.test("the cat sat") // true /\bcat\b/.test("category") // false
\BThe inverse of \b — matches everywhere a word boundary doesn't. Rarely reached for directly, but useful for matching inside a word without touching its edges.
m flag, ^ and $ anchor to the start and end of the entire input, not each line — a multi-line string with no m flag only has one ^ and one $, at the very beginning and very end. See the Flags & Modes section below.Character Classes
The building blocks — each one matches exactly one character. Everything more complex in a pattern is these, combined.
.Matches any single character except line terminators — unless the s flag is set, in which case it matches those too.
\dMatches any digit, equivalent to [0-9]. \D is the negation — any non-digit.
\wMatches any 'word' character: letters, digits, and underscore — equivalent to [A-Za-z0-9_]. \W is the negation.
\sMatches any whitespace character — spaces, tabs, newlines. \S is the negation, any non-whitespace character.
[abc]A custom character class — matches any single character that appears inside the brackets. [abc] matches exactly one of 'a', 'b', or 'c', not the sequence 'abc'.
[^abc]A negated character class — matches any single character that is not one of the listed ones. The ^ only means 'negate' as the very first character inside the brackets.
[a-z]A range inside a character class. Ranges can be combined and mixed with literal characters: [a-zA-Z0-9_-] matches letters, digits, underscore, or hyphen.
., *, and + are just literal characters — [.] matches only a literal dot, not "any character." The characters that keep special meaning inside [ ] are ^ (negation, first position only), - (range, unless it's first/last or escaped), and ] itself (needs \] unless it's first).Quantifiers
How many times the thing right before the quantifier is allowed to repeat.
*Matches the preceding token 0 or more times, greedily — as many times as possible before backtracking.
+Matches the preceding token 1 or more times, greedily. The most common quantifier in practice.
?Matches the preceding token 0 or 1 times — makes it optional. (Also reused as part of non-capturing groups, lookaround, and lazy quantifiers — context decides which meaning applies.)
{n}, {n,}, {n,m}Exact repetition counts: {n} exactly n times, {n,} n or more times, {n,m} between n and m times inclusive.
/\d{3}/.test("42") // false, needs exactly 3 digits /\d{3}/.test("123") // true
*? +? ?? {n,m}?Adding ? after a quantifier makes it lazy instead of greedy — it matches as few characters as possible instead of as many.
const html = "<a><b>"; html.match(/<.+>/)?.[0] // "<a><b>" (greedy — grabs to the last >) html.match(/<.+?>/)?.[0] // "<a>" (lazy — stops at the first >)
"<.+>" against "<a><b>" grabs the whole string, not just "<a>" — .+ greedily eats everything, then backtracks only as far as it must to still find a trailing >. Fix it with a lazy quantifier (.+?) or, often better, a negated character class ([^>]+) that structurally can't cross the boundary you care about.Groups & Capturing
Parentheses do two jobs at once: they scope part of a pattern together, and — unless you opt out — they remember what matched.
(...)A capturing group. Groups a subpattern (so a quantifier or alternation applies to the whole thing) and saves what it matched, accessible as match[1] or, in a replacement string, $1.
(?:...)A non-capturing group — groups without saving the match. Use this whenever you need the grouping behavior but don't care about extracting that piece; it keeps your numbered groups predictable as you add more.
(?<name>...)A named capturing group. Access it as match.groups.name instead of a numbered index — considerably more readable once a pattern has more than one or two groups.
const m = "2026-07-08".match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/); m.groups.year // "2026" m.groups.month // "07"
\1 and $1\1 backreferences group 1 later in the same pattern (e.g. matching a repeated word). $1 backreferences group 1 inside a replacement string passed to .replace().
"07/08/2026".replace(/(\d{2})\/(\d{2})\/(\d{4})/, "$3-$1-$2") // "2026-07-08"
$<name> works in a replacement string too, e.g. "$<year>-$<month>-$<day>", so a reformatting replace can read as clearly as the pattern that produced it.Alternation & Logic
Regex's version of 'or' — and it's easy to reach for the wrong tool between this and a character class.
a|bMatches a or b. The alternation extends as far left and right as the surrounding group or pattern boundary allows, which surprises people the first time.
(?:cat|dog)Wrapping alternatives in a group scopes the | to just what's inside it. Without the group, cat|dog anywhere inside a longer pattern applies to everything on either side of the pipe, not just the word.
[cd]at and (?:cat|dog) look similar but do different jobs: a character class picks between single characters at one position (matches "cat" or "dat", not "dog"), while alternation picks between whole subpatterns. Reach for a character class when only one character varies, and grouped alternation when whole chunks of the pattern differ.Lookahead & Lookbehind
Assertions that check what's next to the current position without becoming part of the match — the part of regex that trips up almost everyone at first.
(?=...)Positive lookahead — asserts that what follows matches the pattern, without consuming those characters as part of the overall match.
(?!...)Negative lookahead — asserts that what follows does NOT match the pattern.
(?<=...)Positive lookbehind — asserts that what precedes the current position matches the pattern, again without consuming those characters.
(?<!...)Negative lookbehind — asserts that what precedes does NOT match the pattern.
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$A password-strength check built entirely from lookahead: each (?=...) checks a condition — has a lowercase letter, has an uppercase letter, has a digit — without moving the match position forward, so all three can 'start' checking from the same spot before .{8,} finally consumes the string.
Flags & Modes
Single letters after the closing slash that quietly change what every symbol in the pattern means.
gGlobal — finds every match instead of stopping after the first. Required for .replace() and .match() to affect/return more than one occurrence.
iCase-insensitive — 'Cat' and 'cat' both match /cat/i.
mMultiline — ^ and $ match the start/end of each line in the string instead of just the very start/end of the whole string.
sdotAll — makes . also match newline characters, which it excludes by default.
uUnicode mode — treats the pattern as Unicode code points rather than UTF-16 code units, and enables \u{...} escapes. Matters for patterns dealing with emoji or characters outside the Basic Multilingual Plane.
ySticky — matches only starting exactly at lastIndex, without scanning ahead for the next possible match. Used for building tokenizers that consume a string piece by piece.
"a,b,c".replace(/,/, "-") produces "a-b,c" — only the first comma. Without the g flag, both .replace() and a plain .match() stop after one hit. See Troubleshooting below.Common Patterns
Ready-made patterns for the cases that come up constantly — copy, don't reinvent.
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/A loose email match — good enough for 'does this look roughly like an email' form feedback. Not RFC 5322 complete, and no regex truly is.
/https?:\/\/[^\s/$.?#].[^\s]*/giMatches http:// or https:// URLs. The [^\s/$.?#] after the scheme rejects an empty or malformed host instead of matching an empty string.
/\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}/A US-style phone number, tolerant of (555) 123-4567, 555-123-4567, and 555.123.4567 formatting.
/^\d{4}-\d{2}-\d{2}$/An ISO 8601 date (YYYY-MM-DD). Checks the shape, not that the date is real — 2026-02-31 still passes.
/\b(?:\d{1,3}\.){3}\d{1,3}\b/A loose IPv4 address match. Checks the four-dotted-numbers shape, not that each number is actually 0-255 — 999.999.999.999 would still match.
/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/A hex color code, accepting both the 3-digit shorthand (#fff) and full 6-digit form (#ffffff).
/^[a-z0-9]+(-[a-z0-9]+)*$/A kebab-case slug — lowercase letters and digits, hyphen-separated, no leading/trailing/double hyphens.
real@example.com is actually someone's real inbox. Use it for instant form feedback, and pair it with an actual verification email for anything that matters.Quick Reference
Already know regex — just blanked on a token? Search instead of scrolling.
^Start of the string (or line, with the m flag)
$End of the string (or line, with the m flag)
\bWord boundary
\BNon-word boundary
.Any character except line terminators (unless s flag)
\dAny digit, 0-9
\DAny non-digit
\wAny word character (letters, digits, underscore)
\WAny non-word character
\sAny whitespace character
\SAny non-whitespace character
[abc]Any one of the listed characters
[^abc]Any character except the listed ones
[a-z]Any character in the given range
*0 or more, greedy
+1 or more, greedy
?0 or 1, optional
{n}Exactly n times
{n,}n or more times
{n,m}Between n and m times, inclusive
*?0 or more, lazy — matches as little as possible
+?1 or more, lazy — matches as little as possible
(...)Capturing group
(?:...)Non-capturing group
(?<name>...)Named capturing group
\1Backreference to group 1, inside the pattern
$1Backreference to group 1, inside a replacement string
$<name>Backreference to a named group, inside a replacement string
a|bMatches a or b
(?:cat|dog)Grouped alternation, scoped to just those words
(?=...)Positive lookahead
(?!...)Negative lookahead
(?<=...)Positive lookbehind
(?<!...)Negative lookbehind
gGlobal — find all matches, not just the first
iCase-insensitive matching
mMultiline — ^ and $ match per line
sdotAll — . also matches newline characters
uUnicode mode — enables \u{...} escapes
ySticky — matches only starting at lastIndex
[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}Loose email match
https?:\/\/[^\s/$.?#].[^\s]*Loose URL match starting with http(s)
\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}US-style phone number
^\d{4}-\d{2}-\d{2}$ISO date (YYYY-MM-DD)
\b(?:\d{1,3}\.){3}\d{1,3}\bLoose IPv4 address
^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$Hex color code
^[a-z0-9]+(-[a-z0-9]+)*$kebab-case slug
47 of 47 commands
Common Use Cases
Real situations, not just isolated tokens — the exact pattern and call that gets the job done.
You need to validate an email field without a 40-line RFC pattern
Use the loose pattern from Common Patterns as instant UX feedback — then actually confirm via email:
- /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value)
You want every URL in a block of text, not just the first
Add the g flag and use matchAll to get every match with capture data:
- [...text.matchAll(/https?:\/\/[^\s]+/g)]
You need to reformat a date from MM/DD/YYYY to YYYY-MM-DD
Capture the three parts, then reorder them in the replacement string:
- date.replace(/(\d{2})\/(\d{2})\/(\d{4})/, "$3-$1-$2")
You want to split a string on commas, semicolons, or extra whitespace
A character class inside split() handles every delimiter in one pass:
- str.split(/[,;\s]+/).filter(Boolean)
You need a password to satisfy several independent rules
Chain lookaheads instead of writing separate checks for each rule:
- /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/.test(pw)
You want to strip everything except digits from a messy phone number
A negated character class with the g flag removes every non-digit in one call:
- raw.replace(/\D+/g, "")
Debugging & Troubleshooting
The symptoms a broken regex actually produces, decoded.
.* matched way more of the string than expectedQuantifiers are greedy by default — .* grabs to the end of the string, then backtracks only as far as it must. Use a lazy quantifier (.*?) or, usually better, a negated character class (e.g. [^>]+ instead of .+, as in the Quantifiers section above) that structurally can't cross the boundary you actually care about.
Only the first match got replaced or foundAdd the g (global) flag. Without it, .replace() and .match() both stop after the very first match.
str.replace(/pattern/g, "replacement")^ and $ aren't matching each line, only the whole stringBy default ^ and $ anchor to the start and end of the entire input. Add the m (multiline) flag to make them match the start/end of every line instead.
/pattern/mSyntaxError: Invalid regular expression: Unterminated groupA ( was never closed, or a literal ( in your pattern wasn't escaped. Count your parentheses, and escape any literal one you actually mean to match as \(.
The page freezes or the tab becomes unresponsive while matchingCatastrophic backtracking — usually from a nested quantifier like (a+)+ or (a*)* tested against a long string that almost, but doesn't quite, match. The engine tries an exponential number of ways to split the string between the nested groups. Rewrite to remove the ambiguous nesting, or use a single quantifier instead of one repetition wrapping another.
A pattern works in the Regex Tester but breaks when built from a variable in codeSpecial characters in a dynamically-built pattern need escaping before they reach new RegExp() — an unescaped ., *, or ( from user input changes what the pattern actually matches, or throws entirely.
const escaped = str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')Things to Remember
If you forget everything else on this page, keep these.
- Character classes ([abc]) pick between single characters; alternation (a|b) picks between whole subpatterns — they aren't interchangeable.
- Quantifiers are greedy by default — add ? to make one lazy when you want the shortest possible match, not the longest.
- Forgetting the g flag is the most common reason a regex "only works once" instead of matching every occurrence.
- Lookaround checks what's next to a position without consuming it — several can chain at the same spot as independent conditions.
- Escape any dynamic or user-provided string before it reaches new RegExp() — unescaped input changes what the pattern matches.
- Nested quantifiers on ambiguous patterns — like (a+)+ — can freeze the page with catastrophic backtracking. Avoid repetition inside repetition.
- Regex is for flat, regular patterns. Reaching for it to parse nested structures like HTML or JSON is a well-known way to grow a monster.
- Test every pattern here live, against your own text, in the Regex Tester before shipping it.
Ready to try one of these against real text? Open the Regex Tester.
Common Use Cases
About Regex Cheat Sheet
A regex is the fastest way to turn a five-line validation function into a single line of code nobody — including you, in six months — can read at a glance. Most regex cheat sheets don't help: an alphabetized dump of tokens with no sense of how they combine, written for someone who already understands regex well enough not to need the cheat sheet.
This one is organized the way a pattern is actually built. Character classes and anchors first — the atoms, the things that match a single position or character. Quantifiers and groups next — how you repeat and combine those atoms into something bigger. Then the two places almost everyone gets tripped up: lookaround (matching a position based on what's next to it, without consuming it) and flags (the single-letter switches that quietly change what every symbol in your pattern means). It closes with a set of ready-made patterns — email, URL, phone number, hex color — for the common cases where writing one from scratch is a waste of time.
Every section calls out the mistakes that actually cause bugs: a greedy .* that swallows far more than intended, a forgotten g flag that silently replaces only the first match instead of all of them, nested quantifiers that freeze the tab with catastrophic backtracking. There's a searchable quick-reference table for the moment you already know what you're looking for, and a troubleshooting section built around the actual symptoms a broken regex produces — not a paraphrase of them.
Every example here uses JavaScript's regex flavor, the one AllCoreKit's own Regex Tester tool runs on — so once a pattern makes sense on this page, you can paste it straight into that tool and watch it match live against your own text. Like every tool on this site, both pages run entirely in your browser: nothing you search for or test is ever sent anywhere or logged.
Frequently Asked Questions
Related Tools
View all Cheat SheetsGit Cheat Sheet
The Git commands developers actually use daily, organized by workflow — branching, rebasing, undoing mistakes, and recovery — with real examples and a searchable quick reference.
Prompt Engineering Cheat Sheet
The prompt patterns that actually change LLM output — anatomy, few-shot, chain-of-thought, and structured formatting — with real examples, pitfalls, and a searchable quick reference.
Linux Commands Cheat Sheet
The Linux commands developers actually use daily — navigation, permissions, piping, processes, and networking — with real examples, common mistakes, and a searchable quick reference.