Regular Expressions

A regular expression ("regex") is a pattern for matching text. With one compact line you can check whether a string looks like a postcode, find every hashtag in a post, or reformat dates. The syntax looks cryptic at first, but a small core covers most practical needs.

Creating a regex

JS create.js
const literal = /cat/;
const fromString = new RegExp("cat");

console.log(literal.test("concatenate"));
console.log(fromString.test("dog"));
Output
true
false

A regex literal is written between slashes. The RegExp constructor is useful when the pattern is built from a variable. test() returns true if the pattern appears anywhere in the string.

Flags

Letters after the closing slash change how matching works:

FlagMeaning
iCase-insensitive
gGlobal — find all matches, not just the first
mMulti-line — ^ and $ match at each line
u / vFull Unicode support (emoji, character properties)
s. also matches newlines
JS flags.js
console.log(/hello/.test("Hello"));
console.log(/hello/i.test("Hello"));
console.log("a1b2c3".replace(/\d/, "#"));
console.log("a1b2c3".replace(/\d/g, "#"));
Output
false
true
a#b2c3
a#b#c#

Character classes

PatternMatches
.Any character except newline
\d / \DA digit / a non-digit
\w / \WA word character (letter, digit, _) / anything else
\s / \SWhitespace / non-whitespace
[abc]Any one of a, b or c
[a-z], [0-9]A range
[^abc]Any character except a, b or c

Quantifiers and anchors

PatternMeaning
*0 or more
+1 or more
?0 or 1 (optional)
{3}, {2,4}, {2,}Exactly 3, between 2 and 4, at least 2
^ / $Start / end of the string
\bA word boundary
a|ba or b
JS validate.js
const zip = /^\d{5}(-\d{4})?$/;
console.log(zip.test("90210"), zip.test("90210-1234"), zip.test("9021"));

const hexColor = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
console.log(hexColor.test("#D6336C"), hexColor.test("#fff"), hexColor.test("red"));

const username = /^[a-z][a-z0-9_]{2,15}$/;
console.log(username.test("ada_99"), username.test("9lives"));
Output
true true false
true true false
true false

Without ^ and $, a pattern can match part of a string — /\d{5}/ would accept "abc123456xyz". Anchor validation patterns at both ends.

Finding matches

JS match.js
const post = "Loving #javascript and #webdev today! #js";

console.log(post.match(/#\w+/));
console.log(post.match(/#\w+/g));
console.log(post.search(/#/));
Output
[
  '#javascript',
  index: 7,
  input: 'Loving #javascript and #webdev today! #js',
  groups: undefined
]
[ '#javascript', '#webdev', '#js' ]
7

Without g, match returns details of the first match (or null). With g, it returns an array of every matching string.

Capture groups

Parentheses capture part of a match. Named groups, written (?<name>…), make the result much more readable:

JS groups.js
const date = "Due: 2026-10-06";
const m = date.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);

console.log(m.groups.year, m.groups.month, m.groups.day);
console.log(date.replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1"));
Output
2026 10 06
Due: 06/10/2026

In a replacement string, $1, $2… refer to the captured groups.

Looping over all matches

JS match-all.js
const prices = "Tea $3.50, Cake $4.25, Juice $5";

for (const m of prices.matchAll(/(\w+) \$(\d+(?:\.\d+)?)/g)) {
  console.log(m[1], "→", Number(m[2]));
}
Output
Tea → 3.5
Cake → 4.25
Juice → 5

(?:…) groups without capturing. matchAll requires the g flag and gives full details, including groups, for every match.

Replacing with a function

JS replace-fn.js
const text = "the quick brown fox";
console.log(text.replace(/\b\w/g, ch => ch.toUpperCase()));

const message = "Order 3 apples and 12 pears";
console.log(message.replace(/\d+/g, n => String(Number(n) * 2)));
Output
The Quick Brown Fox
Order 6 apples and 24 pears

Escaping special characters

Characters like . * + ? ( ) [ ] { } ^ $ | \ / have special meanings. To match them literally, escape them with a backslash: /\$\d+\.\d{2}/ matches $4.99. When building a pattern from user input, escape it first — newer engines provide RegExp.escape() for exactly this.

Tip: regexes are easy to get subtly wrong. Test them on real examples — including ones that should fail — and prefer simple string methods (includes, startsWith, split) when they're enough. Don't try to fully validate email addresses with a regex; check for an @ and send a confirmation email instead.

Try it yourself

Try it yourself
Console output