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.

runs locally on your browser. Your data never leaves your device.
12 sections
47 tokens & patterns
Searchable quick reference
100% offline

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

^
Essential

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

$
Essential

Matches the very end of the string. With the m flag, matches the end of every line instead.

\b
Handy

Matches 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

\B
Advanced

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

^ and $ default to the whole string
Without the 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.

.
Essential

Matches any single character except line terminators — unless the s flag is set, in which case it matches those too.

\d
Essential

Matches any digit, equivalent to [0-9]. \D is the negation — any non-digit.

\w
Essential

Matches any 'word' character: letters, digits, and underscore — equivalent to [A-Za-z0-9_]. \W is the negation.

\s
Essential

Matches any whitespace character — spaces, tabs, newlines. \S is the negation, any non-whitespace character.

[abc]
Handy

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]
Handy

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]
Handy

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.

Most special characters go inert inside [ ]
Inside a character class, ., *, 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.

*
Essential

Matches the preceding token 0 or more times, greedily — as many times as possible before backtracking.

+
Essential

Matches the preceding token 1 or more times, greedily. The most common quantifier in practice.

?
Essential

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}
Handy

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}?
Advanced

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 >)

Greedy is the default, and it's the #1 cause of 'matched too much'
A pattern like "<.+>" 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.

(...)
Essential

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.

(?:...)
Handy

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>...)
Handy

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
Advanced

\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"

Pro trick
Named groups aren't just for reading — $<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|b
Essential

Matches 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)
Handy

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.

Character class vs. alternation — not the same tool
[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.

(?=...)
Advanced

Positive lookahead — asserts that what follows matches the pattern, without consuming those characters as part of the overall match.

(?!...)
Advanced

Negative lookahead — asserts that what follows does NOT match the pattern.

(?<=...)
Advanced

Positive lookbehind — asserts that what precedes the current position matches the pattern, again without consuming those characters.

(?<!...)
Advanced

Negative lookbehind — asserts that what precedes does NOT match the pattern.

^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$
Advanced

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.

Note
Because a lookaround doesn't consume characters, several of them can be chained back-to-back at the same position — each one is an independent yes/no check, not a step forward through the string. That's what makes them the right tool for "must contain X AND must contain Y" without caring what order X and Y appear in.

Flags & Modes

Single letters after the closing slash that quietly change what every symbol in the pattern means.

g
Essential

Global — finds every match instead of stopping after the first. Required for .replace() and .match() to affect/return more than one occurrence.

i
Essential

Case-insensitive — 'Cat' and 'cat' both match /cat/i.

m
Handy

Multiline — ^ and $ match the start/end of each line in the string instead of just the very start/end of the whole string.

s
Handy

dotAll — makes . also match newline characters, which it excludes by default.

u
Advanced

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

y
Advanced

Sticky — 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.

Forgetting g is the single most common regex bug
"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,}/
Essential

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]*/gi
Essential

Matches 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}/
Handy

A US-style phone number, tolerant of (555) 123-4567, 555-123-4567, and 555.123.4567 formatting.

/^\d{4}-\d{2}-\d{2}$/
Handy

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/
Handy

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})$/
Handy

A hex color code, accepting both the 3-digit shorthand (#fff) and full 6-digit form (#ffffff).

/^[a-z0-9]+(-[a-z0-9]+)*$/
Advanced

A kebab-case slug — lowercase letters and digits, hyphen-separated, no leading/trailing/double hyphens.

A regex can't verify an email exists
The email pattern above (and every other regex like it) checks shape, not deliverability — it can't tell you 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)

\b

Word boundary

\B

Non-word boundary

.

Any character except line terminators (unless s flag)

\d

Any digit, 0-9

\D

Any non-digit

\w

Any word character (letters, digits, underscore)

\W

Any non-word character

\s

Any whitespace character

\S

Any 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

\1

Backreference to group 1, inside the pattern

$1

Backreference to group 1, inside a replacement string

$<name>

Backreference to a named group, inside a replacement string

a|b

Matches a or b

(?:cat|dog)

Grouped alternation, scoped to just those words

(?=...)

Positive lookahead

(?!...)

Negative lookahead

(?<=...)

Positive lookbehind

(?<!...)

Negative lookbehind

g

Global — find all matches, not just the first

i

Case-insensitive matching

m

Multiline — ^ and $ match per line

s

dotAll — . also matches newline characters

u

Unicode mode — enables \u{...} escapes

y

Sticky — 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}\b

Loose 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:

  1. /^[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:

  1. [...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:

  1. 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:

  1. str.split(/[,;\s]+/).filter(Boolean)

You need a password to satisfy several independent rules

Chain lookaheads instead of writing separate checks for each rule:

  1. /^(?=.*[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:

  1. raw.replace(/\D+/g, "")

Debugging & Troubleshooting

The symptoms a broken regex actually produces, decoded.

.* matched way more of the string than expected

Quantifiers 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 found

Add 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 string

By 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/m
SyntaxError: Invalid regular expression: Unterminated group

A ( 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 matching

Catastrophic 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 code

Special 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

Recall the exact syntax for a named capture group or lookahead before writing a validation pattern
Figure out why a pattern is only matching once instead of every occurrence in a string
Grab a ready-made pattern for emails, hex colors, or phone numbers instead of writing one from scratch
Search the quick-reference table for a token's exact meaning instead of digging through MDN

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