JS RegExp
Regex in JavaScript: literals, the RegExp constructor, flags, capture groups, lookarounds, named captures, replaceAll, and matchAll.
Regex — JavaScript essentials
EXAMPLE
// ===== Two ways to make a regex =====
const lit = /\bworld\b/gi; // literal, fastest, compiled once
const dyn = new RegExp('\\bworld\\b', 'gi'); // dynamic, double-escaped
// ===== Flags =====
// g global (use across whole string; required for replaceAll / matchAll on regex)
// i ignore case
// m multiline (^ and $ match per line)
// s dotall (. matches \n)
// u Unicode (\p{...}, \u{...}, \w covers Unicode letters)
// y sticky (must match at lastIndex)
// d hasIndices (provides match.indices)
// ===== Test and match =====
/cat/.test('catwalk'); // true (boolean)
'catwalk'.match(/cat/); // ['cat', index: 0, input: ...]
'catwalk dogwalk'.match(/walk/g); // ['walk', 'walk']
[...'catwalk dogwalk'.matchAll(/(\w+)walk/g)]; // each match with capture group
// matchAll yields RegExpMatchArray objects with groups + index.
// ===== Capture groups =====
const m = 'order #42 total $49.95'.match(/order #(\d+) total \$(\d+\.\d{2})/);
m[0]; // whole match
m[1]; // '42'
m[2]; // '49.95'
// Named groups:
const m2 = 'iso 2024-04-10'.match(/(?<y>\d{4})-(?<mo>\d{2})-(?<d>\d{2})/);
m2.groups.y; // '2024'
// Non-capturing:
const m3 = 'abc123'.match(/(?:abc)(\d+)/);
m3[1]; // '123'
// ===== Replace =====
'hello WORLD'.replace(/world/i, 'web'); // 'hello web'
'foo foo'.replaceAll('foo', 'bar'); // 'bar bar' (string mode)
'hello WORLD'.replaceAll(/world/gi, 'web'); // requires g flag with RegExp
// Replace with a function:
'$49.95 and $3.10'.replace(/\$(\d+)\.(\d{2})/g, (_, dollars, cents) => {
return \`${(+dollars * 100 + +cents)} cents\`;
});
// Named backref in replacement:
'2024-04-10'.replace(/(?<y>\d{4})-(?<mo>\d{2})-(?<d>\d{2})/, '$<d>/$<mo>/$<y>');
// '10/04/2024'
// ===== Lookarounds =====
// (?=...) positive lookahead, (?!...) negative lookahead
// (?<=...) positive lookbehind, (?<!...) negative lookbehind
'price: $49'.match(/(?<=\$)\d+/); // ['49'] (lookbehind for $)
'pickAB pickXY'.match(/pick(?=A)/g); // ['pick']
'pickAB pickXY'.match(/pick(?!A)/g); // ['pick']
// ===== Unicode =====
'café'.match(/\w+/u); // ['café'] (with /u)
'café'.match(/\w+/); // ['caf'] (without /u)
'abc 中文'.match(/\p{L}+/gu); // ['abc', '中文']
// ===== exec for multi-pass with lastIndex =====
const re = /\d+/g;
let mm;
while ((mm = re.exec('a1 b22 c333')) !== null) {
console.log(mm[0], mm.index);
}
// ===== Common patterns =====
// Email-ish /^[^\s@]+@[^\s@]+\.[^\s@]+$/
// URL host /^https?:\/\/([^/]+)/
// ISO date /^\d{4}-\d{2}-\d{2}$/
// Hex colour /^#([0-9a-f]{3}|[0-9a-f]{6})$/i
// ===== Pitfalls =====
// - Forgetting /g with replaceAll on a RegExp -> throws
// - Building dynamic regex from user input without escaping -> ReDoS / injection
// - .* across newlines without /s -> 'why doesn't it match?'
// - Catastrophic backtracking: (a+)+$ on 'aaaa...X' is exponential
// - Mixing test() and global flags -> shares lastIndex, surprising behaviour on second call
// - Lookbehind on older Safari/iOS: check support before pinning it in critical code
// ===== Patterns to internalise =====
// - Anchor with ^ and $ when matching the whole string
// - Prefer named groups when more than two captures appear
// - matchAll for iteration; .match(/g) only gives strings, no indices
// - Escape user input that becomes a regex (helper: s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
Why it matters
JavaScript regex is mostly the standard set with /u, /d, named groups, and matchAll on top. Compile literals where possible, escape dynamic patterns, anchor strictly, and reach for lookarounds when you need context-sensitive matches. The flags + named groups + matchAll combo covers nearly every quick text job without reaching for a parser.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
const re = /(?<area>\d{3})-(?<num>\d{4})/;
const m = '555-867-5309'.match(re);
console.log(m?.groups?.area, m?.groups?.num);
Try it Yourself »
Discussion
Loading…