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 match text. You can use it to search, validate, and replace parts of a string without writing long manual checks.

Think of regex as a smart text filter. Instead of matching one exact word, you define the pattern you want, like "starts with a number, then letters, then @".

You create a regex in JavaScript in two ways:

javascript

// 1. Regex literal - the most common way
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

Regex is not just a JavaScript thing - it exists in almost every programming language. Once you understand it here, the core ideas carry over everywhere.

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

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] - any lowercase letter
  • [A-Z] - any uppercase letter
  • [0-9] - any digit
  • [a-zA-Z0-9] - any letter or digit

There are also built-in shorthand classes:

  • \d - any digit (same as [0-9])
  • \D - any non-digit
  • \w - any word character: letters, digits, 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 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

By default, quantifiers are greedy - they match as much as possible. Add ? after a quantifier to make it lazy (match as little as possible):

javascript

const str = "bold and more";

// Greedy: matches as much as possible
const greedy = str.match(/.*<\/b>/);
console.log(greedy[0]); // bold and more  (too much)

// Lazy: matches as little as possible
const lazy = str.match(/.*?<\/b>/);
console.log(lazy[0]);   // bold  (just the first)

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

  • ^ - 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

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

Parentheses (...) create a capturing group. They let you apply a quantifier to a whole sub-pattern, and also capture the matched text so you can use it later.

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 - Unicode mode
  • d - return start/end indices for each match (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) - returns array of matches (or null)
  • str.matchAll(regex) - returns iterator of all matches with details (requires 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 (requires g flag)
  • 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 not a perfect RFC-compliant validator, but it covers the vast majority of real-world inputs.

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
const phoneRegex = /^\(?\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("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).
  • 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.
  • 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?

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.