Regular expressions are a small language for describing text patterns. This guide covers the parts of JavaScript’s regex syntax you’ll use most, with patterns you can paste into the regex tester. It runs your pattern in the browser’s own engine, highlights matches and shows capture groups.
The building blocks
| Pattern | Matches |
|---|---|
abc | the literal text “abc” |
. | any character except a line break (with the s flag, any character at all) |
\d \w \s | a digit, a “word” character [A-Za-z0-9_], whitespace |
\D \W \S | the opposite of each |
[aeiou] | one of the listed characters |
[^0-9] | any character except those listed |
[a-z] | a range |
^ $ | start and end of the input (of each line with the m flag) |
\b | a word boundary |
\. \( \\ | a literal ., ( or \, escaped |
Quantifiers: how many
| Pattern | Meaning |
|---|---|
a* | 0 or more |
a+ | 1 or more |
a? | 0 or 1 (optional) |
a{3} | exactly 3 |
a{2,4} | 2 to 4 |
a{2,} | 2 or more |
Quantifiers are greedy: they match as much as possible. Add ? to make them lazy. On <b>one</b><b>two</b>, <b>.*</b> matches the whole string, while <b>.*?</b> matches each tag pair separately.
Groups and alternatives
(abc)is a capturing group. It remembers what matched, as$1,$2… in replacements.(?:abc)groups without capturing. Use it with|or a quantifier when you don’t need the value.(?<area>\d{3})is a named group, available asmatch.groups.areaand$<area>in replacements.cat|dogmatches either alternative.\1or\k<area>matches the same text a group already captured, e.g.(\w)\1for a doubled letter.
Lookarounds: match only if…
| Pattern | Meaning |
|---|---|
x(?=y) | x, only if followed by y |
x(?!y) | x, only if not followed by y |
(?<=y)x | x, only if preceded by y |
(?<!y)x | x, only if not preceded by y |
Lookarounds check the surrounding text without including it in the match. For example, (?<=R)\d+ matches the number in “R250” but not the “R”.
Flags
| Flag | Effect |
|---|---|
g | find all matches, not just the first |
i | case-insensitive |
m | ^ and $ match at line breaks |
s | . also matches line breaks |
u | Unicode mode: correct handling of emoji and \p{…} property escapes like \p{L} (any letter) |
v | an upgraded u mode with set operations in classes (newer engines) |
y | “sticky”: match only at lastIndex |
d | include start and end indices for groups |
Worked examples
South African phone numbers in formats like “082 123 4567”, “082-123-4567” or “0821234567”:
(?<area>0\d{2})[\s-]?(\d{3})[\s-]?(\d{4})
Replace with +27 $<area> $2 $3 and then remove the leading 0 to normalise them. The regex tester opens with this example.
Find duplicated words (a common typo, “the the”):
\b(\w+)\s+\1\b
Use it with the gi flags.
Trim spaces at the start or end of each line: ^[ \t]+|[ \t]+$ with gm, replaced with nothing.
Pull amounts out of text: R\s?(\d{1,3}(?:[ ,]\d{3})*(?:\.\d{2})?) matches “R1 250.00”, “R 85” and “R12,500”.
Slug-safe text: replace [^\p{L}\p{N}]+ (with the gu flags) with -, then lowercase the result. The slug generator does this for you, and handles accents too.
In JavaScript code
const re = /(?<area>0\d{2})[\s-]?(\d{3})[\s-]?(\d{4})/g;
for (const m of text.matchAll(re)) console.log(m.groups.area, m.index);
const tidy = text.replace(re, '+27 $<area> $2 $3');
When you build a pattern from a string, double the backslashes, or use String.raw:
new RegExp(String.raw`\d{3}-\d{4}`, 'g');
Pitfalls
lastIndexwithgory: callingre.test()repeatedly on a global regex continues from the last match, so results alternate between true and false. Create the regex fresh, or resetlastIndex = 0.- Forgetting to escape:
.matches any character. To match “1.5” exactly, write1\.5. - Catastrophic backtracking: nested quantifiers like
(a+)+$or(\w+\s?)*$can take exponential time on input that almost matches. That can freeze a browser tab, or a server (a ReDoS). Avoid nesting repeated groups that can match the same text. The regex tester runs patterns in a background worker and stops them after 1.5 seconds, so you’ll see the problem without your tab hanging. - Validating everything with one regex: a simple pattern plus code that’s easy to read beats a 300-character expression nobody can maintain.
Practise
Open the regex tester, paste some real text and build your pattern one piece at a time, checking the highlights as you go. To transform the matches afterwards, the text case converter and character counter are handy next steps.