Regex Cheatsheet: Complete Reference for JavaScript Regular Expressions
A complete reference for JavaScript regular expressions — from basic syntax to advanced patterns. Use Regex Tester to try any pattern as you read. Jump to a section:
- Anchors
- Character Classes
- Quantifiers
- Groups and References
- Lookahead and Lookbehind
- Flags
- JavaScript RegExp Methods
- Common Pattern Library
Anchors
Anchors match a position, not a character.
| Syntax | Matches | Example |
|---|---|---|
^ | Start of string (or line with m flag) | /^hello/ matches "hello world", not "say hello" |
$ | End of string (or line with m flag) | /world$/ matches "hello world", not "world peace" |
\b | Word boundary (between \w and \W) | /\bcat\b/ matches "cat" but not "catch" |
\B | Non-word boundary | /\Bcat\B/ matches "concatenate" |
// Match exact string (anchored both ends)
/^hello world$/.test("hello world") // true
/^hello world$/.test("hello world!") // false
// Word boundary: match "cat" as a whole word
const text = "The cat catalog";
text.match(/cat/g) // ["cat"] — only the standalone word
text.match(/cat/g) // ["cat", "cat"] — matches inside "catalog" tooCharacter Classes
Shorthand Classes
| Syntax | Matches | Equivalent |
|---|---|---|
. | Any character except newline (\n) | Use [\s\S] to include newlines |
\d | Digit | [0-9] |
\D | Non-digit | [^0-9] |
\w | Word character | [a-zA-Z0-9_] |
\W | Non-word character | [^a-zA-Z0-9_] |
\s | Whitespace (space, tab, newline, CR, FF, VT) | [ \t\n\r\f\v] |
\S | Non-whitespace | [^ \t\n\r\f\v] |
\n | Newline | |
\t | Tab |
Custom Character Classes [...]
| Syntax | Matches |
|---|---|
[abc] | a, b, or c |
[^abc] | Any character except a, b, or c |
[a-z] | Any lowercase letter |
[A-Z] | Any uppercase letter |
[0-9] | Any digit |
[a-zA-Z0-9] | Any alphanumeric character |
[a-z&&[^aeiou]] | Consonants only (not standard JS — use explicit list) |
// Match a hex color digit
/[0-9a-fA-F]/.test("f") // true
/[0-9a-fA-F]/.test("g") // false
// Match anything except digits
/[^d]+/.exec("abc123") // ["abc"]
// Inside character class: ^ negates, - is range, ] ends class
// To include literal ], put it first: []abc]
// To include literal -, put it last: [a-z-] or first: [-a-z]
// To include literal ^, don't put it first: [a^b]Unicode Categories (with u flag)
// Unicode property escapes require the 'u' flag
/p{Letter}/u.test("é") // true — matches any Unicode letter
/p{Decimal_Number}/u.test("٣") // true — Arabic-Indic digit 3
/p{Emoji}/u.test("😊") // true
// Useful properties:
// p{L} or p{Letter} — any Unicode letter
// p{N} or p{Number} — any Unicode number
// p{Z} or p{Separator} — any Unicode separator
// p{Sc} — currency symbols ($, €, £, ¥)
// p{Script=Latin} — Latin script charactersQuantifiers
Standard Quantifiers
| Syntax | Matches | Example |
|---|---|---|
* | 0 or more (greedy) | /a*/ matches "", "a", "aaa" |
+ | 1 or more (greedy) | /a+/ matches "a", "aaa", not "" |
? | 0 or 1 (optional) | /colou?r/ matches "color" and "colour" |
{n} | Exactly n times | /\d{4}/ matches exactly 4 digits |
{n,} | n or more times | /\d{2,}/ matches 2 or more digits |
{n,m} | Between n and m times | /\d{2,4}/ matches 2–4 digits |
Greedy vs Lazy vs Possessive
| Mode | Syntax | Behaviour |
|---|---|---|
| Greedy | * + ? {n,m} | Match as much as possible, then backtrack |
| Lazy | *? +? ?? {n,m}? | Match as little as possible |
const html = "<b>bold</b> and <i>italic</i>";
// Greedy: matches from first < to last >
html.match(/<.+>/)[0] // "<b>bold</b> and <i>italic</i>"
// Lazy: matches from < to nearest >
html.match(/<.+?>/g) // ["<b>", "</b>", "<i>", "</i>"]
// Practical: extract content between tags
html.match(/<b>(.+?)</b>/)[1] // "bold"Groups and References
Capturing Groups (...)
| Syntax | Description |
|---|---|
(abc) | Capturing group — captures match for backreference |
(?:abc) | Non-capturing group — groups without capturing |
(?<name>abc) | Named capturing group |
\1 \2… | Backreference to group 1, 2… within the pattern |
\k<name> | Named backreference |
$1 $2… | Group reference in replace() string |
$<name> | Named group reference in replace() |
// Capturing group — access via match result
const m = "2026-03-05".match(/(d{4})-(d{2})-(d{2})/);
m[1] // "2026" (year)
m[2] // "03" (month)
m[3] // "05" (day)
// Named capturing groups
const m2 = "2026-03-05".match(/(?<year>d{4})-(?<month>d{2})-(?<day>d{2})/);
m2.groups.year // "2026"
m2.groups.month // "03"
m2.groups.day // "05"
// Non-capturing group — group without capturing (for alternation)
/(?:Mr|Mrs|Ms) Smith/.test("Ms Smith") // true
// (no captured group in result)
// Backreference: match repeated word
/(w+) \1/.test("the the") // true — matches doubled words
/(w+) \1/.test("the cat") // false
// Named backreference
/(?<tag>w+)>.*</\k<tag>/.test("<div>hello</div>") // true
/(?<tag>w+)>.*</\k<tag>/.test("<div>hello</span>") // falseAlternation |
// Match either alternative
/cat|dog/.test("I have a dog") // true
/cat|dog/.test("I have a cat") // true
// Alternation with groups
/^(GET|POST|PUT|PATCH|DELETE)$/.test("POST") // true
/^(GET|POST|PUT|PATCH|DELETE)$/.test("SEND") // false
// Order matters: first match wins (left to right)
"abc".match(/a|ab/) // ["a"] — "a" matched first
"abc".match(/ab|a/) // ["ab"] — "ab" matched firstLookahead and Lookbehind
Lookaround assertions match a position where a condition is true, without consuming characters. They are zero-width — the characters they check are not included in the match.
| Type | Syntax | Matches position where… |
|---|---|---|
| Positive lookahead | X(?=Y) | X is followed by Y |
| Negative lookahead | X(?!Y) | X is NOT followed by Y |
| Positive lookbehind | (?<=Y)X | X is preceded by Y |
| Negative lookbehind | (?<!Y)X | X is NOT preceded by Y |
// Positive lookahead: match "foo" only when followed by "bar"
"foobar".match(/foo(?=bar)/) // ["foo"] — Y not included
"foobaz".match(/foo(?=bar)/) // null
// Negative lookahead: match price digits not followed by %
"$100 50%".match(/d+(?!%)/g) // ["100"] (50 is excluded)
// Positive lookbehind: match digits preceded by $
"$100 €200".match(/(?<=$)d+/) // ["100"]
// Negative lookbehind: match digits NOT preceded by $
"$100 200".match(/(?<!$)d+/g) // ["200"]
// Practical: insert thousands separator
"1234567".replace(/B(?=(d{3})+(?!d))/g, ",") // "1,234,567"
// Practical: extract value after a label
"Price: 49.99 USD".match(/(?<=Price: )[d.]+/) // ["49.99"]
// Practical: password validation — must contain digit
/^(?=.*d)/.test("abc123") // true
/^(?=.*d)/.test("abcdef") // falseFlags
| Flag | Name | Effect |
|---|---|---|
g | Global | Find all matches, not just first. Enables matchAll(). |
i | Case-insensitive | a matches A and a |
m | Multiline | ^ and $ match start/end of each line |
s | Dotall | . matches newline (\n) too |
u | Unicode | Enable full Unicode matching and \p{...} properties |
v | UnicodeSets (ES2024) | Superset of u: set operations, string properties |
y | Sticky | Match only at lastIndex, no search forward |
d | Indices (ES2022) | Include start/end indices for each group in match() result |
// g — find all matches
"aababc".match(/a/g) // ["a", "a", "a"]
"aababc".match(/a/) // ["a"] — only first
// i — case-insensitive
/hello/i.test("Hello World") // true
/hello/i.test("HELLO") // true
// m — multiline anchors
const text = "line1
line2
line3";
text.match(/^w+/gm) // ["line1", "line2", "line3"]
text.match(/^w+/g) // ["line1"] — only first line start
// s — dotall: . matches newline
/<div>(.*?)</div>/s.exec("<div>
hello
</div>")[1] // "
hello
"
// u — unicode
/😀/u.test("😀") // true
/p{Emoji}/u.test("😀") // true
// d — capture group indices (ES2022)
const result = "foo bar".match(/(?<word>w+)/d);
result.indices.groups.word // [0, 3] — start and end positionsJavaScript RegExp Methods
| Method | Returns | Notes |
|---|---|---|
regex.test(str) | boolean | Does the pattern match? |
regex.exec(str) | array | null | First match with groups; advances lastIndex with g |
str.match(regex) | array | null | Without g: like exec; with g: all matches, no groups |
str.matchAll(regex) | iterator | All matches with groups; requires g flag |
str.replace(regex, repl) | string | Replace first (or all with g) |
str.replaceAll(regex, repl) | string | Replace all; requires g flag |
str.search(regex) | number | -1 | Index of first match |
str.split(regex) | array | Split on pattern |
// test: boolean check
/d+/.test("abc123") // true
/d+/.test("abc") // false
// exec: access groups one match at a time
const re = /(w+)=(w+)/g;
let m;
while ((m = re.exec("a=1 b=2 c=3")) !== null) {
console.log(m[1], "→", m[2]);
}
// "a" → "1"
// "b" → "2"
// "c" → "3"
// matchAll: all matches with groups (modern preferred)
const pairs = [...'a=1 b=2 c=3'.matchAll(/(?<key>w+)=(?<val>w+)/g)];
pairs.map(m => ({ [m.groups.key]: m.groups.val }));
// [{ a: "1" }, { b: "2" }, { c: "3" }]
// replace with function
"hello WORLD".replace(/w+/g, w => w[0].toUpperCase() + w.slice(1).toLowerCase());
// "Hello World"
// replace with named group reference
"2026-03-05".replace(
/(?<y>d{4})-(?<m>d{2})-(?<d>d{2})/,
"$<d>/$<m>/$<y>"
); // "05/03/2026"
// split on multiple separators
"one,two;three|four".split(/[,;|]/) // ["one", "two", "three", "four"]Escaping Special Characters
These characters have special meaning in regex and must be escaped with \ to match literally:
. ^ $ * + ? { } [ ] \ | ( )// Match a literal dot (not any character)
/3.14/.test("3.14") // true
/3.14/.test("3X14") // false
// Escape user input for use in regex
function escapeRegex(str) {
return str.replace(/[.*+?^${}()|[]\]/g, '\$&');
}
const userInput = "1+1=2";
new RegExp(escapeRegex(userInput)).test("1+1=2") // trueCommon Pattern Library
Test all patterns in the Regex Tester. Each pattern below is written to be practical over theoretically perfect — regex alone cannot fully validate most formats; use it for filtering and initial validation.
Email Address
// Practical email validation (not RFC 5321 complete, but covers 99.9% of real addresses)
const email = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*.[a-zA-Z]{2,}$/;
email.test("user@example.com") // true
email.test("user.name+tag@sub.co.uk") // true
email.test("user@") // false
email.test("@example.com") // falseURL
// Match http/https URLs
const url = /https?://(www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}([-a-zA-Z0-9()@:%_+.~#?&/=]*)/;
url.test("https://example.com") // true
url.test("http://sub.example.co.uk/path?q=1#hash") // true
url.test("ftp://example.com") // false
// Extract all URLs from text
const text = "Visit https://foo.com or http://bar.org for details.";
text.match(/https?://[^s)>]+/g);
// ["https://foo.com", "http://bar.org"]IPv4 Address
// Match valid IPv4 (0-255 per octet)
const ipv4 = /^((25[0-5]|2[0-4]d|1d{2}|[1-9]d|d).){3}(25[0-5]|2[0-4]d|1d{2}|[1-9]d|d)$/;
ipv4.test("192.168.1.1") // true
ipv4.test("255.255.255.0") // true
ipv4.test("256.0.0.1") // false
ipv4.test("192.168.1") // falseIPv6 Address
// Full and compressed IPv6
const ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|::([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|::)$/;
ipv6.test("2001:db8::1") // true
ipv6.test("::1") // true (loopback)
ipv6.test("fe80::1%eth0") // false (zone IDs not included)Date Formats
// ISO 8601: YYYY-MM-DD
const isoDate = /^d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]d|3[01])$/;
isoDate.test("2026-03-05") // true
isoDate.test("2026-13-01") // false (month 13)
// US format: MM/DD/YYYY
const usDate = /^(0[1-9]|1[0-2])/(0[1-9]|[12]d|3[01])/d{4}$/;
usDate.test("03/05/2026") // true
// Flexible: match common separators (-, /, .)
const flexDate = /^d{4}[-/.](0[1-9]|1[0-2])[-/.](0[1-9]|[12]d|3[01])$/;
flexDate.test("2026.03.05") // trueTime
// 24-hour time HH:MM or HH:MM:SS
const time24 = /^([01]d|2[0-3]):([0-5]d)(:([0-5]d))?$/;
time24.test("23:59") // true
time24.test("23:59:59") // true
time24.test("24:00") // false
// 12-hour time with AM/PM
const time12 = /^(0?[1-9]|1[0-2]):[0-5]ds?(AM|PM|am|pm)$/;
time12.test("12:30 PM") // true
time12.test("3:00am") // truePhone Numbers
// US phone: (123) 456-7890 or 123-456-7890 or +1 123 456 7890
const usPhone = /^(+1s?)?((?d{3})?[s.-]?)d{3}[s.-]?d{4}$/;
usPhone.test("(123) 456-7890") // true
usPhone.test("+1 123 456 7890") // true
usPhone.test("123.456.7890") // true
// International E.164 format
const e164 = /^+[1-9]d{1,14}$/;
e164.test("+12025551234") // true
e164.test("+447911123456") // trueHex Color
// 3 or 6 digit hex colors (with optional #)
const hexColor = /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
hexColor.test("#ff0000") // true
hexColor.test("#f00") // true
hexColor.test("ff0000") // true
hexColor.test("#gggggg") // false
// Also match 8-digit (with alpha)
const hexColorAlpha = /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;UUID
// UUID v4 (random)
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
uuid.test("550e8400-e29b-41d4-a716-446655440000") // true
// Any UUID version
const anyUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;Semantic Version
// semver: MAJOR.MINOR.PATCH with optional pre-release and build metadata
const semver = /^(0|[1-9]d*).(0|[1-9]d*).(0|[1-9]d*)(?:-((?:0|[1-9]d*|d*[a-zA-Z-][0-9a-zA-Z-]*)(?:.(?:0|[1-9]d*|d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:+([0-9a-zA-Z-]+(?:.[0-9a-zA-Z-]+)*))?$/;
semver.test("1.0.0") // true
semver.test("2.3.4-alpha.1") // true
semver.test("1.0.0+build.123") // true
semver.test("1.0") // falseCredit Card Number
// Visa
const visa = /^4[0-9]{12}(?:[0-9]{3})?$/;
// Mastercard
const mastercard = /^5[1-5][0-9]{14}|^2(2[2-9][1-9]|2[3-9][0-9]|[3-6][0-9]{2}|7[01][0-9]|720)[0-9]{12}$/;
// Any major card (strip spaces/dashes first)
const anyCard = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12})$/;
// Always strip spaces before testing
"4111 1111 1111 1111".replace(/s/g, ""); // "4111111111111111"Password Strength Rules
// At least 8 chars, one uppercase, one lowercase, one digit, one special char
const strongPassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[!@#$%^&*()_+-=[]{};':"\|,.<>/?]).{8,}$/;
strongPassword.test("Passw0rd!") // true
strongPassword.test("password") // false (no uppercase, digit, special)
strongPassword.test("PASSWORD1!") // false (no lowercase)
// Check individual rules with separate patterns (better UX than one mega-regex)
const rules = {
minLength: str => str.length >= 8,
hasUpper: str => /[A-Z]/.test(str),
hasLower: str => /[a-z]/.test(str),
hasDigit: str => /d/.test(str),
hasSpecial: str => /[!@#$%^&*]/.test(str),
};Slug (URL-Friendly String)
// Valid URL slug: lowercase letters, digits, hyphens
const slug = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
slug.test("my-blog-post") // true
slug.test("hello-world-2026") // true
slug.test("My Post") // false
// Convert a string to slug
function toSlug(str) {
return str
.toLowerCase()
.trim()
.replace(/[^ws-]/g, '') // remove non-word chars
.replace(/[s_-]+/g, '-') // spaces/underscores → hyphens
.replace(/^-+|-+$/g, ''); // trim leading/trailing hyphens
}
toSlug("Hello, World! 2026") // "hello-world-2026"Extract Content Between Tags
// Extract inner HTML of a specific tag
const html = "<p>First</p><p>Second</p>";
// All matches
html.match(/<p>(.*?)</p>/g) // ["<p>First</p>", "<p>Second</p>"]
// Inner content only (with matchAll)
[...html.matchAll(/<p>(.*?)</p>/g)].map(m => m[1]) // ["First", "Second"]
// Multiline content (requires s flag)
const multi = "<div>
Hello
</div>";
multi.match(/<div>(.*?)</div>/s)[1] // "
Hello
"Trim Whitespace Variants
// Trim leading and trailing whitespace (like str.trim())
str.replace(/^s+|s+$/g, '')
// Collapse internal whitespace to single space
str.replace(/s+/g, ' ')
// Remove all whitespace
str.replace(/s/g, '')
// Trim and collapse in one step
str.replace(/^s+|s+$/g, '').replace(/s+/g, ' ')Multiline Comments
// Strip C-style block comments /* ... */
code.replace(//*[sS]*?*//g, '')
// Strip single-line comments // ...
code.replace(///.*$/gm, '')
// Strip both
code
.replace(//*[sS]*?*//g, '')
.replace(///.*$/gm, '')Debugging Tips
- Test incrementally — build the pattern piece by piece in Regex Tester with real input, not after writing the full expression.
- Catastrophic backtracking — patterns like
(a+)+or(\w+\s?)*against a failing string can take exponential time. Avoid nested quantifiers on the same character class. - Anchors prevent partial matches — if
/\d+/matches unexpectedly, add^and$to require the whole string to be digits. lastIndexreuse bug — regex objects withgflag maintain state vialastIndex. Callingtest()orexec()repeatedly on the same regex instance will cycle through matches. Useregex.lastIndex = 0to reset, or create a new regex object each time.- Flags are case-sensitive —
/pattern/Gis a syntax error; flags must be lowercase.
Try any pattern from this cheatsheet in Regex Tester — paste your pattern and test string to see match highlights, group captures, and flag behavior instantly.