Regular Expressions in JavaScript
Regular expressions (regex) are patterns used to match character combinations in strings. In JavaScript, they’re a powerful tool for validation, search, and text manipulation. If you’ve ever needed to check if an email looks real, extract URLs from text, or find all the numbers in a string, regex is the answer.
Creating a Regular Expression
You can create a regex in two ways. The literal syntax is most common:
const pattern = /hello/;
Or you can use the RegExp constructor when the pattern comes from a variable:
const searchTerm = "hello";
const pattern = new RegExp(searchTerm);
Both produce a RegExp object. The literal syntax is cleaner for fixed patterns.
Basic Matching
At its simplest, a regex matches exact characters:
const regex = /hello/;
console.log(regex.test("hello world")); // true
console.log(regex.test("goodbye")); // false
The dot . matches any single character:
const regex = /c.t/;
console.log(regex.test("cat")); // true
console.log(regex.test("cut")); // true
console.log(regex.test("cart")); // false
Character Classes
Square brackets let you match specific sets of characters:
// Match any vowel
const vowelRegex = /[aeiou]/;
console.log(vowelRegex.test("bcd")); // false
console.log(vowelRegex.test("axe")); // true
// Match any digit
const digitRegex = /[0-9]/;
console.log(digitRegex.test("year2024")); // true
// Negate with ^
const notDigit = /[^0-9]/;
console.log(notDigit.test("abc")); // true
console.log(notDigit.test("123")); // false
Predefined character classes make common patterns shorter:
\w // Word character: [a-zA-Z0-9_]
\W // Non-word character
\d // Digit: [0-9]
\D // Non-digit
\s // Whitespace (space, tab, newline)
\S // Non-whitespace
. // Any character except newline
const hasDigit = /\d/;
const isWord = /\w+/;
console.log(hasDigit.test("hello")); // false
console.log(isWord.test("hello_123")); // true
Anchors
Anchors don’t match characters — they match positions in the string:
^ // Start of string
$ // End of string
\b // Word boundary
const startsWithNum = /^\d/;
console.log(startsWithNum.test("123abc")); // true
console.log(startsWithNum.test("abc123")); // false
const endsWithNum = /\d$/;
console.log(endsWithNum.test("abc123")); // true
console.log(endsWithNum.test("123abc")); // false
const wholeWord = /\bhello\b/;
console.log(wholeWord.test("hello world")); // true
console.log(wholeWord.test("helloworld")); // false
Quantifiers
Quantifiers specify how many times something should match:
* // 0 or more
+ // 1 or more
? // 0 or 1 (optional)
{n} // Exactly n times
{n,} // n or more times
{n,m} // Between n and m times
// Match one or more digits
const digits = /\d+/;
console.log(digits.test("a123b")); // true
console.log(digits.test("abc")); // false
// Match optional 's' at end
const plural = /dogs?/;
console.log(plural.test("dog")); // true
console.log(plural.test("dogs")); // true
// Match 3-5 letter word
const threeToFive = /\b\w{3,5}\b/;
console.log(threeToFive.test("hi")); // false
console.log(threeToFive.test("cat")); // true
console.log(threeToFive.test("elephant")); // false
Greedy vs lazy matching matters. By default, quantifiers are greedy — they match as much as possible:
const greedy = /".*"/; // Greedy
const lazy = /".*?"/; // Lazy (add ?)
const text = '"hello" "world"';
console.log(text.match(greedy)); // ['"hello" "world"']
console.log(text.match(lazy)); // ['"hello"', '"world"']
Groups and Backreferences
Parentheses create capturing groups, which let you extract parts of a match:
const dateRegex = /(\d{4})-(\d{2})-(\d{2})/;
const match = "2024-03-11".match(dateRegex);
console.log(match[0]); // "2024-03-11" (full match)
console.log(match[1]); // "2024" (first group)
console.log(match[2]); // "03" (second group)
console.log(match[3]); // "11" (third group)
You can reference groups later with \1, \2, etc:
// Match repeated words
const repeatedWord = /\b(\w+)\s\1\b/;
console.log(repeatedWord.test("the the")); // true
console.log(repeatedWord.test("the cat")); // false
Non-capturing groups use (?:...) when you need grouping but don’t want to capture:
const regex = /(?:Mr|Mrs|Ms)\. (\w+)/;
const match = "Mr. Smith".match(regex);
console.log(match[1]); // "Smith"
Flags
Flags modify how the regex behaves:
g // Global — find all matches, not just first
i // Case-insensitive
m // Multiline — ^ and $ match line starts/ends
s // Dotall — . matches newlines
u // Unicode — enables Unicode features
y // Sticky — match at lastIndex position only
// Case insensitive
const caseInsensitive = /hello/i;
console.log(caseInsensitive.test("HELLO")); // true
console.log(caseInsensitive.test("Hello")); // true
// Global - find all matches
const allDigits = /\d/g;
console.log("abc123def456".match(allDigits)); // ['1', '2', '3', '4', '5', '6']
// Multiline
const multiLine = /^line/m;
console.log("line1\nline2\nline".match(multiLine)); // ['line', 'line']
The u flag is essential for Unicode matching and enables Unicode property escapes:
const emojiRegex = /\p{Emoji}/u;
console.log(emojiRegex.test("😀")); // true
console.log(emojiRegex.test("a")); // false
Working with Regex in JavaScript
RegExp Methods
The test() method returns true or false:
const hasUpper = /[A-Z]/;
console.log(hasUpper.test("hello")); // false
console.log(hasUpper.test("Hello")); // true
The exec() method returns match details or null:
const regex = /(\d{4})-(\d{2})-(\d{2})/;
const result = regex.exec("Date: 2024-03-11");
if (result) {
console.log(result[0]); // "2024-03-11"
console.log(result.index); // 6
}
String Methods
The match() method returns an array of matches:
const str = "I have 3 cats and 5 dogs";
const numbers = str.match(/\d+/g);
console.log(numbers); // ['3', '5']
The replace() and replaceAll() methods let you substitute text:
// Replace first occurrence
const result1 = "hello world".replace(/o/, "x");
console.log(result1); // "hellx world"
// Replace all with global flag
const result2 = "hello world".replace(/o/g, "x");
console.log(result2); // "hellx wxrld"
// Use captured groups in replacement
const result3 = "2024-03-11".replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1");
console.log(result3); // "11/03/2024"
// Replacement function
const result4 = "abc123def".replace(/\d+/g, (match) => parseInt(match) * 2);
console.log(result4); // "abc246def"
The split() method uses regex as delimiter:
console.log("a,b;c:d".split(/[,;:/]/)); // ['a', 'b', 'c', 'd']
console.log("hello world".split(/\s+/)); // ['hello', 'world']
The search() method returns the index of the match:
console.log("hello world".search(/wor/)); // 6
console.log("hello world".search(/foo/)); // -1 (not found)
The matchAll() method returns an iterator of all matches with groups:
const regex = /(\d+)/g;
const str = "numbers: 1, 2, 3";
const matches = [...str.matchAll(regex)];
console.log(matches[0]); // ['1', '1', index: 9, ...]
console.log(matches[1]); // ['2', '2', index: 12, ...]
console.log(matches[2]); // ['3', '3', index: 15, ...]
Practical Examples
Email Validation
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
console.log(isValidEmail("test@example.com")); // true
console.log(isValidEmail("invalid@")); // false
console.log(isValidEmail("no@space here.com")); // false
Extracting URLs
const text = "Visit https://example.com or http://test.org for more info";
const urlRegex = /https?:\/\/[^\s]+/g;
const urls = text.match(urlRegex);
console.log(urls); // ['https://example.com', 'http://test.org']
Phone Number Formatting
function formatPhone(phone) {
const digits = phone.replace(/\D/g, "");
const match = digits.match(/^(\d{3})(\d{3})(\d{4})$/);
if (match) {
return `(${match[1]}) ${match[2]}-${match[3]}`;
}
return phone;
}
console.log(formatPhone("5551234567")); // "(555) 123-4567"
console.log(formatPhone("555-123-4567")); // "(555) 123-4567"
console.log(formatPhone("5551234")); // "5551234" (invalid)
Password Strength
function isStrongPassword(password) {
const hasUpper = /[A-Z]/.test(password);
const hasLower = /[a-z]/.test(password);
const hasDigit = /\d/.test(password);
const hasSpecial = /[!@#$%^&*]/.test(password);
const longEnough = password.length >= 8;
const score = [hasUpper, hasLower, hasDigit, hasSpecial, longEnough]
.filter(Boolean).length;
return score >= 4;
}
console.log(isStrongPassword("abc123")); // false
console.log(isStrongPassword("Abc123!")); // true
console.log(isStrongPassword("SecureP@ss1")); // true
Common Pitfalls
Escaping Special Characters
Some characters have special meaning and need escaping:
// Match a literal dot
const dot = /\./;
console.log(dot.test("hello.world")); // true
// Match literal brackets
const brackets = /\[.*\]/;
console.log(brackets.test("[test]")); // true
Performance
Complex patterns can cause performance issues with catastrophic backtracking:
// Problematic: nested quantifiers can cause exponential backtracking
const badPattern = /(a+)+b/;
// Better: simpler pattern
const goodPattern = /a+b/;
Lookahead and Lookbehind
Lookaheads check what comes next without including it in the match:
// Positive lookahead: must be followed by "world"
const regex = /hello(?= world)/;
console.log(regex.test("hello world")); // true
console.log(regex.test("hello there")); // false
// Negative lookahead: must NOT be followed by "world"
const regex2 = /hello(?! world)/;
console.log(regex2.test("hello world")); // false
console.log(regex2.test("hello there")); // true
Lookbehind (ES2018+) checks what comes before:
// Positive lookbehind: must be preceded by "$"
const regex = /(?<=\$)\d+/;
console.log(regex.exec("price is $100")); // ["100"]
// Negative lookbehind: must NOT be preceded by "$"
const regex2 = /(?<!\$)\d+/;
console.log(regex2.test("$100")); // false
console.log(regex2.test("a100")); // true
See Also
- JavaScript String Methods — Learn about string manipulation methods that work with regex
- JavaScript Closures Explained — Understand scope, which is essential for advanced regex patterns
- Asynchronous JavaScript — See how regex fits into async workflows