Regular Expressions in JavaScript

Why should you care about Regular Expressions?

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:

javascript

// 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.

javascript

// 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:

javascript

// . 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:

Token-by-token explanation of the date pattern.
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

javascript

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
Build gradually: test \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

javascript

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

javascript

// + 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:

javascript

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.

Tools for creating and checking JavaScript regular expressions.
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.
  1. Write the smallest pattern that could work.
  2. Add a passing example, a near-miss, an empty string, and an unexpected-character case.
  3. Turn on only the flags you need and check whether anchors are required.
  4. Copy the tested pattern into JavaScript and verify test, match, or replace behavior.
Safety note: do not paste passwords, access tokens, private customer data, or other secrets into a public online tester. For sensitive text, use the browser console or a local tool.

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.

javascript

// ^ 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.

javascript

// 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:

javascript

// (?: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:

javascript

// 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)

javascript

// 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

javascript

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

javascript

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"]
Practical use case: Validate an email address format - /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email). This is a basic check, not a perfect validator, but it covers most real-world cases.

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.

javascript

// [^\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.

javascript

// \(? - 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.

javascript

// [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's next? Head over to the Error Handling tutorial to learn how to catch and handle errors in JavaScript.

Reviewed by

SimplyJavaScript Editorial Team

Technical editors and JavaScript educators with hands-on experience building frontend projects, writing learning material, and reviewing tutorials for clarity, accuracy, and beginner-friendly guidance.