Regular Expressions in JavaScript
Regex helps you validate forms, clean messy input, and find patterns in text quickly. Once you learn the basics, many everyday string tasks become much easier.
A regular expression (regex) is a pattern used to search, validate, extract, or replace text. You can use it to find structured text without writing long manual checks.
A regex defines the pattern that JavaScript should find. For example, a pattern can require text to start with a number, contain letters, and include an @.
You create a regex in JavaScript in two ways:
// 1. Regex literal - convenient for a pattern known when writing the code
const pattern = /hello/;
// 2. RegExp constructor - useful when the pattern is dynamic
const word = "hello";
const pattern2 = new RegExp(word);
// Test a string against the pattern
console.log(pattern.test("say hello there")); // true
console.log(pattern.test("goodbye")); // false
Use a regex literal when the pattern is known while writing the code. Use the RegExp constructor when the pattern must be built dynamically from a string; it is not necessary for ordinary static patterns.
Regex is used in many programming languages. The core regex concepts are similar across many programming languages, but syntax and supported features can differ.
A regex literal is written between two forward slashes: /pattern/. After the closing slash, you can add flags like g, i, or m to control matching behavior.
// Pattern: match the word "cat" (case-sensitive)
const pattern = /cat/;
console.log(pattern.test("The cat sat")); // true
console.log(pattern.test("The Cat sat")); // false - uppercase C
// With the i flag: case-insensitive
const patternI = /cat/i;
console.log(patternI.test("The Cat sat")); // true
Special characters in regex have reserved meanings - for example ^ and $ are anchors, * + ? and {} are quantifiers, and () creates a group. These are all covered in the sections below. If you want to match one of these characters literally, escape it with a backslash:
// . without escape matches ANY character
/c.t/.test("c@t"); // true - . matches @
// \. matches a literal dot
/c\.t/.test("c.t"); // true
/c\.t/.test("c@t"); // false
Read a regex from left to right. Start with the boundaries, then identify each group, separator, and repetition rule. For example, this pattern accepts a date written as 123-45-6789:
^\d{3}-\d{2}-\d{4}$
^start
\d{3}3 digits
-hyphen
\d{2}2 digits
-hyphen
\d{4}4 digits
$end
| Token | Meaning | Matches |
|---|---|---|
^ |
Start anchor | Nothing before the pattern |
\d |
One ASCII digit | 0 through 9 |
{3}, {2}, {4} |
Exact repetition counts | Three, two, and four digits |
- |
Literal hyphen | The separator character itself |
$ |
End anchor | Nothing after the pattern |
const datePattern = /^\d{3}-\d{2}-\d{4}$/;
console.log(datePattern.test("123-45-6789")); // true
console.log(datePattern.test("123456789")); // false: separators missing
console.log(datePattern.test("x123-45-6789")); // false: extra text before start
\d, then \d{3}, then add the separators and anchors. Smaller steps make a failed match easier to debug.
Character classes let you match one character from a group. You create them with square brackets [...].
- [abc] - matches a, b, or c
- [^abc] - matches any single character that is not a, b, or c (a negated character class)
- [a-z] - one lowercase ASCII letter in that range
- [A-Z] - one uppercase ASCII letter in that range
- [0-9] - one ASCII digit
- [a-zA-Z0-9] - any letter or digit
There are also built-in shorthand classes:
- \d - any ASCII digit (same as [0-9])
- \D - any non-digit
- \w - any ASCII word character: letters, digits, or underscore (same as [a-zA-Z0-9_])
- \W - any non-word character
- \s - any whitespace (space, tab, newline)
- \S - any non-whitespace
- . - any character except line terminators such as newline
console.log(/\d/.test("abc3")); // true - contains a digit
console.log(/\d/.test("abcdef")); // false - no digits
console.log(/\w/.test("hello_1")); // true
console.log(/\s/.test("hi there")); // true - has a space
console.log(/[aeiou]/.test("sky")); // false - no vowels
Quantifiers control how many times a character or group must appear to count as a match.
- * - 0 or more times
- + - 1 or more times
- ? - 0 or 1 time (makes it optional)
- {n} - exactly n times
- {n,} - at least n times
- {n,m} - between n and m times
// + means one or more digits
console.log(/\d+/.test("abc123")); // true
console.log(/\d+/.test("abcdef")); // false
// {3} means exactly 3 digits
console.log(/\d{3}/.test("12")); // false
console.log(/\d{3}/.test("123")); // true
console.log(/\d{3}/.test("1234")); // true - "123" matches inside it
// ? makes the 's' optional
console.log(/colou?r/.test("color")); // true
console.log(/colou?r/.test("colour")); // true
Advanced: Greedy and lazy quantifiers become especially useful when a pattern can match different amounts of text. Greedy quantifiers generally match as much as possible while still allowing the overall pattern to succeed. Add ? after a quantifier to make it lazy, so it generally matches as little as possible while still allowing the overall pattern to succeed:
const str = "<b>bold</b> and <b>more</b>";
// Greedy: matches as much as possible
const greedy = str.match(/<b>.*<\/b>/);
console.log(greedy[0]); // <b>bold</b> and <b>more</b> (too much)
// Lazy: matches as little as possible
const lazy = str.match(/<b>.*?<\/b>/);
console.log(lazy[0]); // <b>bold</b> (just the first)
A regex tester gives you highlighted matches, explanations, and quick feedback while you build a pattern. Select the ECMAScript/JavaScript flavor when using an online tester, and always confirm the final pattern in JavaScript because regex flavors differ.
| Tool | Best use | Important detail |
|---|---|---|
| Regex101 | Detailed explanations, match groups, and debugging | Select the ECMAScript (JavaScript) flavor; the default flavor may support features JavaScript does not. |
| RegExr | Interactive highlighting and a quick reference | Use its JavaScript flavor and test several positive and negative examples. |
| Browser DevTools Console | Final JavaScript behavior | Run /pattern/.test("sample") and verify the exact code you will ship. |
| MDN | Confirm JavaScript syntax and browser behavior | Use it as the reference when a tester's explanation is unclear. |
- Write the smallest pattern that could work.
- Add a passing example, a near-miss, an empty string, and an unexpected-character case.
- Turn on only the flags you need and check whether anchors are required.
- Copy the tested pattern into JavaScript and verify
test,match, orreplacebehavior.
Anchors do not match characters. They match a position in the string. This lets you say "this pattern must appear at the start" or "must appear at the end".
- ^ outside a character class - matches the start of the string
- $ - matches the end of the string
- \b - matches a word boundary (between a word character and a non-word character)
- \B - matches a non-word boundary
The meaning of ^ depends on where it appears. Outside square brackets, as in ^abc, it anchors the match to the start of the string. Immediately after [, as in [^abc], it negates the character class and matches one character that is not a, b, or c.
// ^ anchors to the start
console.log(/^Hello/.test("Hello world")); // true
console.log(/^Hello/.test("Say Hello")); // false
// $ anchors to the end
console.log(/world$/.test("Hello world")); // true
console.log(/world$/.test("world peace")); // false
// ^ and $ together: exact full-string match
console.log(/^\d{5}$/.test("12345")); // true - exactly 5 digits
console.log(/^\d{5}$/.test("1234")); // false
console.log(/^\d{5}$/.test("123456")); // false
// \b word boundary
console.log(/\bcat\b/.test("the cat sat")); // true
console.log(/\bcat\b/.test("concatenate")); // false
Advanced: Parentheses (...) create a capturing group. They let you apply a quantifier to a whole sub-pattern and store the matched text so it can be accessed from the match result.
// Group with quantifier: (ab)+ means "ab" repeated one or more times
console.log(/(ab)+/.test("ababab")); // true
console.log(/(ab)+/.test("aab")); // false
// Capture groups in match()
const date = "2026-04-06";
const match = date.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(match[0]); // "2026-04-06" - full match
console.log(match[1]); // "2026" - group 1
console.log(match[2]); // "04" - group 2
console.log(match[3]); // "06" - group 3
Use (?:...) for a non-capturing group when you need grouping for a quantifier but don't need to capture the value:
// (?:ab)+ groups but does not capture
const m = "ababab".match(/(?:ab)+/);
console.log(m[0]); // "ababab" - full match
console.log(m[1]); // undefined - no capture group
You can also use the | (pipe) character as an OR operator inside or outside a group:
// Match "cat" or "dog"
console.log(/cat|dog/.test("I have a dog")); // true
console.log(/cat|dog/.test("I have a fish")); // false
// Group the OR to apply a quantifier
console.log(/(cat|dog)s?/.test("cats")); // true
console.log(/(cat|dog)s?/.test("dogs")); // true
Flags go after the closing slash and change how matching works. You can combine multiple flags.
- i - case-insensitive matching
- g - global: find all matches, not just the first
- m - multiline: ^ and $ match start/end of each line, not the whole string
- s - dot-all: . matches newlines too
- u - enables Unicode-aware matching behavior
- d - Advanced: includes match start/end indices in the result (ES2022)
// i flag - case-insensitive
console.log(/hello/i.test("HELLO WORLD")); // true
// g flag - find all matches
const str = "cat bat sat";
const all = str.match(/[a-z]at/g);
console.log(all); // ["cat", "bat", "sat"]
// Without g - only the first match
const first = str.match(/[a-z]at/);
console.log(first[0]); // "cat"
// m flag - multiline
const multi = "line1\nline2\nline3";
const lines = multi.match(/^\w+/gm);
console.log(lines); // ["line1", "line2", "line3"]
You can use regex with both RegExp methods and String methods.
RegExp methods:
- regex.test(str) - returns true or false
- regex.exec(str) - returns match array with index info, or null
const regex = /\d+/;
console.log(regex.test("abc 123")); // true
console.log(regex.test("no nums")); // false
const result = regex.exec("abc 123 def");
console.log(result[0]); // "123"
console.log(result.index); // 4 - position in string
String methods that accept regex:
- str.match(regex) - without g, returns the first match with capture details; with g, returns an array of matched strings (or null)
- str.matchAll(regex) - Advanced: returns an iterator of all matches with details (requires the g flag)
- str.search(regex) - returns index of first match, or -1
- str.replace(regex, replacement) - replaces match with a string or function
- str.replaceAll(regex, replacement) - replaces all matches; a regex argument requires the g flag, while a string argument does not
- str.split(regex) - splits string at each match
const str = "Hello World 2026";
// match - returns array
console.log(str.match(/\d+/)); // ["2026", index: 12, ...]
console.log(str.match(/\d+/g)); // ["2026"]
// search - returns index
console.log(str.search(/World/)); // 6
console.log(str.search(/xyz/)); // -1
// replace - swap matched text
console.log(str.replace(/World/, "JavaScript")); // "Hello JavaScript 2026"
// replace with a function
const result = "cat and dog".replace(/cat|dog/g, match => match.toUpperCase());
console.log(result); // "CAT and DOG"
// split on whitespace
console.log("one two three".split(/\s+/)); // ["one", "two", "three"]
The best way to understand regex is to see it solve real problems. Here are three common use cases you will encounter in practice.
1. Email address validation
A practical email check confirms there is text before and after the @, and a domain with a dot and extension. It is a basic format check, not a complete RFC-compliant validator. Regex can validate the expected format, but it cannot prove that an email address or phone number actually exists.
// [^\s@]+ - one or more chars that are not whitespace or @
// @ - literal @ symbol
// \. - literal dot (escaped)
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
console.log(emailRegex.test("user@example.com")); // true
console.log(emailRegex.test("name.last@sub.org")); // true
console.log(emailRegex.test("missing-at-sign.com")); // false
console.log(emailRegex.test("no-domain@")); // false
console.log(emailRegex.test("spaces @example.com")); // false
2. Phone number format check (US)
This pattern accepts common US phone formats: 555-123-4567, (555) 123-4567, and 5551234567.
// \(? - optional opening parenthesis
// \d{3} - exactly 3 digits (area code)
// [-.\s]? - optional separator: dash, dot, or space
// The complete area-code group keeps parentheses balanced.
const phoneRegex = /^(?:\(\d{3}\)|\d{3})[-.\s]?\d{3}[-.\s]?\d{4}$/;
console.log(phoneRegex.test("555-123-4567")); // true
console.log(phoneRegex.test("(555) 123-4567")); // true
console.log(phoneRegex.test("5551234567")); // true
console.log(phoneRegex.test("555.123.4567")); // true
console.log(phoneRegex.test("(555-123-4567")); // false - unmatched parenthesis
console.log(phoneRegex.test("12345")); // false
console.log(phoneRegex.test("555-12-4567")); // false
3. URL slug validation
A URL slug should contain only lowercase letters, digits, and hyphens, with no leading or trailing hyphens.
// [a-z0-9] - starts and ends with a lowercase letter or digit
// [a-z0-9-]* - middle can include hyphens
const slugRegex = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
console.log(slugRegex.test("my-blog-post")); // true
console.log(slugRegex.test("about-us")); // true
console.log(slugRegex.test("page1")); // true
console.log(slugRegex.test("-starts-with-hyphen")); // false
console.log(slugRegex.test("has spaces")); // false
console.log(slugRegex.test("MixedCase")); // false
- A regular expression is a pattern that matches text. Create one with a literal /pattern/ or new RegExp("pattern").
- Character classes: [abc] matches one of those chars. Shorthands: \d (digit), \w (word char), \s (whitespace).
- Quantifiers: * (0+), + (1+), ? (0 or 1), {n,m} (range). Add ? after one to make it lazy.
- Anchors: ^ = start, $ = end. Together they force an exact full-string match. \b = word boundary.
- Groups: (abc) captures. (?:abc) groups without capturing. | = OR.
- Flags: i = case-insensitive, g = find all matches, m = multiline anchors.
- Key methods: regex.test() (boolean), str.match() (array), str.replace() (new string), str.split() (array).
- Testing: Build patterns gradually, use a JavaScript-compatible tester, and confirm the final behavior in the browser console.
- Quantifiers are greedy by default - they match as much as possible. Append ? to make them lazy.
- What is a regular expression and what is it used for?
- What is the difference between creating a regex with a literal vs new RegExp()?
- What does the g flag do in a regular expression?
- What is the difference between + and * quantifiers?
- What is the difference between ^ inside square brackets and ^ outside?
- What is a capturing group and how do you access captured text?
- What is the difference between a greedy and a lazy quantifier?
- How would you check if a string contains only digits?
- How would you replace all occurrences of a word in a string using regex?
- What does \b match in a regular expression?