Match Patterns With Regular Expressions in JavaScript

35 min read·Jan 1, 2025

In JavaScript, regular expressions, often abbreviated regex or regexp, are patterns used to match, validate, extract, and replace character combinations in strings.

Creating a regular expression

To create a new regular expression object, you can write a regex literal using the double slash syntax //:

const regex = /pattern/;

Where pattern is a string or regular expression.

Matching patterns in strings

Checking if a pattern exists

To check whether a pattern exists in a string, you can use the test() method of the regex object:

let exists = /pattern/.test(string);

Where:

  • /pattern/ is a regular expression.
  • string is a string of characters.

This function returns true if the pattern exists, and false otherwise.

Listing all matching patterns

To list all the matched occurrences of the pattern in a string, you can use the match() method of the string:

let exists = string.match(/pattern/);

This function returns an array of matches or null if no matches are found.

Matching simple patterns

Simple patterns are substrings, often composed of characters and numbers, that you want to match exactly.

/pattern/

Note: When using simple patterns, the test() method of a regex and the includes() method of a string will produce the same result.

Example

Let's consider this script:

const regex = /the/;
const substring = 'the';
const string = 'The quick brown fox jumps over the lazy dog';

console.log(regex.test(string));           // true
console.log(string.includes(substring));   // true

When executed, it will test whether the substring "the" expressed in the form of a regular expression /the/ exists in the string string.

The wildcard character

The wildcard character . allows you to match any character within a pattern, except line terminators.

/patt.rn/

Note: If you want to match the actual dot character ., you will have to escape it using a backslash character \..

Example

Let's consider this regex:

const regex = /h.llo/;

console.log(regex.test('hello'));   // true
console.log(regex.test('h3llo'));   // true
console.log(regex.test('h@llo'));   // true

When executed, it will match all specified strings as the wildcard character . can be substituted by any character, including 'e', '3', '@', etc.


Let's consider this regex:

const regex = /h\.llo/;

console.log(regex.test('h.llo'));   // true
console.log(regex.test('hello'));   // false

When executed, it will only match the first string "h.llo" as the wildcard character is escaped with a backslash character \, turning it into a literal dot character.

Character classes

Unlike wildcards, character classes allow you to specify a list of possible characters or range of characters.

Specifying a set of characters

To limit the match to a specific set of individual characters, you can use the square brackets syntax:

[<char>...]

Where <char>... is a list of characters (e.g., aeiou).

Example

Let's consider this regex:

const regex = /h[ae]llo/;

console.log(regex.test('hallo'));   // true
console.log(regex.test('hello'));   // true
console.log(regex.test('hxllo'));   // false

When executed, it will match the strings "hallo" and "hello", but not "hxllo", as the regex only allows for the characters 'a', and 'e'.

Specifying a range of characters

To specify a range of characters or digits, you can place a dash character - between the first and last element:

[<start>-<end>...]

Where:

  • <start> is the first character or digit in the range.
  • <end> is the last character or digit in the range.

Note: It is possible to list multiple ranges at once by concatenating them one after the other.

[<char>-<char><num>-<num>]

Example

Let's consider this regex, that matches all lowercase and uppercase letters from 'a' to 'h', then all digits from 1 to 8:

const regex = /[a-hA-H][1-8]/;

console.log(regex.test('a1'));   // true
console.log(regex.test('F3'));   // true

console.log(regex.test('i1'));   // false
console.log(regex.test('X9'));   // false

When executed, it will match the strings "a1" and "F3", however, it will not match the strings "i1" and "X9", as 'i', 'X', and 9 are out of the specified ranges.

Excluding a set of characters

To exclude a character set from the match, you can place a top caret character ^ at the beginning of the set:

[^<set>]

Example

Let's consider this regex, that excludes all lowercase and uppercase vowels as second character:

const regex = /t[^aeiouAEIOU]p./;

console.log(regex.test('type'));   // true
console.log(regex.test('tape'));   // false

When executed, it will match the string "type" as the 'y' character is not in the specified range, but not the string "tape" as the 'a' character is in the excluded range.

Character class escapes

A character class escape is a shorthand that represents a set of characters.

The most common ones are:

  • \d: a shorthand for [0-9].
  • \w: a shorthand for [A-Za-z0-9_].
  • \s: a shorthand for space, tab, and line terminators like \n, \r, etc.

Note: Their uppercase equivalent (i.e. \D, \W, \S) are used to exclude those character sets from the match.

Disjunctions

The disjunction character | allows you to specify multiple alternative patterns at once.

pattern|pattern

Example

Let's consider this regex, that includes the strings "John" or "Jane":

const regex = /John|Jane/;

console.log(regex.test('John Doe'));       // true
console.log(regex.test('Skyller Jane'));   // true
console.log(regex.test('Jack Higgins'));   // false

When executed, it will match the strings "Jack Doe" and "Skyller Jane", but not the string "Jack Higgins".

Boundary-type assertions

A boundary-type assertion is a special character used to define whether the pattern should match at the beginning and/or the end of a string.

To match a string from the beginning of a pattern, you can use the ^ character:

/^pattern/

To match a string from the end of a pattern, you can use the $ character:

/pattern$/

To match a string from both the beginning and the end of a pattern, you can combine both characters:

/^pattern$/

Example

Let's consider this regex, that matches the strings starting with the substring "+33":

const regex = /^\+33/;

console.log(regex.test('+33645874152'));   // true
console.log(regex.test('+41645874152'));   // false

Note: In regex, the + character has a special meaning and must escaped with a backslash character \ to lose its meaning.


Let's consider this regex, that matches the strings ending with the substring ".com" or ".dev":

const regex = /\.com|\.dev$/;

console.log(regex.test('johndoe@mail.com'));       // true
console.log(regex.test('www.learnbackend.dev'));   // true

Quantifiers

A quantifier is a symbol or expression used to specify the amount of times the preceding character, matcher, or group should be repeated:

  • ?: matches the preceding item 0 or 1 times.
  • *: matches the preceding item 0+ times.
  • +: matches the preceding item 1+ times.

Example

Let's consider this regex, that matches the strings "color" or "colour":

const regex = /colou?r/;

console.log(regex.test('The color is blue'));     // true
console.log(regex.test('The colour is black'));   // true

Where u?: matches the 'u' character 0 or 1 times.


Let's consider this regex, that matches any valid URL:

const regex = /^https?:\/\/\w+\.[a-z]+$/;

console.log(regex.test('http://learnbackend.dev'));    // true
console.log(regex.test('https://learnbackend.dev'));   // true
console.log(regex.test('www.learnbackend.dev2'));      // false

Where:

  • ^: indicates the beginning of the expression.
  • https?: matches the strings 'http' or 'https'.
  • :\/\/: matches the string ://.
  • \w+: matches any alphanumeric and underscore character 1 or more times.
  • \.: matches the dot character exactly 1 time.
  • [a-z]+: matches any lowercase letter 1 or more times.
  • $: indicates the end of the expression.

Matching patterns in "non-greedy" mode

By default quantifiers like * and + are "greedy", meaning that they try to match as much of the string as possible.

The ? character after the quantifier makes the quantifier "non-greedy", meaning that it will stop as soon as it finds a match.

For example, given a string like 'some <foo> <bar> new </bar> </foo> thing':

  • /<.*>/ will match "<foo> <bar> new </bar> </foo>".
  • /<.*?>/ will only match "<foo>".

Matching a specific number of elements

To specify exactly, at least, or at most how many times the preceding character, matcher, or group should be repeated, you can use the curly brackets syntax {}:

  • {n}: matches the preceding item exactly n times.
  • {n,}: matches the preceding item at least n times.
  • {n,m}: matches the preceding item at least n times and at most m times.

Example

Let's consider this regex, that matches any valid email address:

const regex = /^[a-zA-Z0-9._%+-]{1,64}@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;

console.log(regex.test('john.doe@mail.com'));        // true
console.log(regex.test('john_doe+test@mail.org'));   // true

console.log(regex.test('john_doe+test@mail'));       // false
console.log(regex.test('john_doe+testmail.org'));    // false

Where:

  • ^: indicates the beginning of the expression.
  • [a-zA-Z0-9._%+-]{1,64}: matches any alphanumeric and special characters (., _, %, +, -) between 1 and 64 times.
  • @: matches the commercial at character exactly 1 time.
  • [a-zA-Z0-9.-]+: matches any alphanumeric and special characters (., -) at least 1 time.
  • \.: matches the dot character exactly 1 time.
  • [a-zA-Z]{2,}: matches any letter at least 2 times.
  • $: indicates the end of the expression.

Groups

A group is a chunk of a regular expression contained in parenthesis () that allows you to group together multiple matchers as a single expression, and eventually capture them to reuse them later:

(pattern)

Capturing groups

To retrieve the substrings matched by the specified groups, you can either use the exec() method of a regular expression:

let matches = regex.exec(string);

Or the .match() method of a string:

let matches = string.match(regex);

Both methods return an array of matches, where:

  • matches[0] is the full match.
  • matches[1+n] is the substring captured by the group(1+n).
  • matches['index'] is the start index of the match.
  • matches['input'] is the original string.
  • matches['groups'] is the list of named groups.

Or null if none of the specified groups match a substring.

Example

Let's consider this script, that extracts the various components of an email address:

const regex = /^([a-zA-Z0-9._%+-]{1,64})@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,})$/;

const matches = regex.exec('support@learnbackend.dev');

console.log(`Email address: '${matches[0]}'`);
console.log(`User name: '${matches[1]}'`);
console.log(`Domain name: '${matches[2]}'`);
console.log(`Domain extension: '${matches[3]}'`);

Where:

  • ([a-zA-Z0-9._%+-]{1,64}) captures the user name.
  • ([a-zA-Z0-9.-]+) captures the domain name.
  • ([a-zA-Z]{2,}) captures the domain extension

Which will produce this output:

Email address: 'support@learnbackend.dev'
User name: 'support'
Domain name: 'learnbackend'
Domain extension: 'dev'

Non-capturing groups

To prevent the capture of groups and ultimately improve the performance of your script, you can prepend the group elements with the ?: expression:

(?:pattern)

Example

Let's consider this regex, that only extracts the domain name of an email address:

const regex = /^(?:https?:\/\/|www\.)([a-zA-Z0-9.-]+)\.[a-zA-Z]{2,}$/;
let matches;
let url;

url = 'http://learnbackend.dev';
matches = regex.exec(url);
console.log(`${url} => ${matches[1]}`);

url = 'https://learn-backend.dev';
matches = regex.exec(url);
console.log(`${url} => ${matches[1]}`);

url = 'www.l3arn.backend.dev';
matches = regex.exec(url);
console.log(`${url} => ${matches[1]}`);

Where:

  • (?:https?:\/\/|www\.): matches one of the strings 'http://', 'https://', or 'www.' without capturing them.
  • ([a-zA-Z0-9.-]+): matches any alphanumeric character and special characters (., -) at least 1 time while capturing them.
  • \.: matches the dot character exactly 1 time.
  • [a-zA-Z]{2,}: matches any letter at least 2 times.

Which will produce this output:

http://learnbackend.dev => learnbackend
https://learn-backend.dev => learn-backend
www.l3arn.backend.dev => l3arn.backend

Lookahead and lookbehind assertions

The lookahead and lookbehind assertions allow you to match a pattern only if it's followed or preceded by another pattern, without including those patterns in the final match.

The lookahead assertion matches "x" only if "x" is followed by "y":

x(?=y)

The negative lookahead assertion matches "x" only if "x" is not followed by "y":

x(?!y)

The lookbehind assertion matches "x" only if "x" is preceded by "y":

(?<=y)x

The negative lookbehind assertion matches "x" only if "x" is not preceded by "y":

(?<!y)x

Example

Let's consider this regex, that matches any string that doesn't start with a digit and doesn't end with a letter:

const regex = /^(?!\d)(?!.*[a-zA-Z]$)\w+$/;

console.log(regex.test('A1B2C3')); // true
console.log(regex.test('1A2B3C')); // false

Where:

  • ^(?!\d): ensures the string doesn't start with a digit.
  • (?!.*[a-zA-Z]$): ensures the string doesn't end with a letter.
  • \w+: ensures the string only contains alphanumeric characters and underscores.

Flags

Flags are used to modify the behavior of the regex pattern.

/pattern/flags

The most common flags are:

  • i: makes the regex case-insensitive, meaning it will match letters regardless of whether they are uppercase or lowercase.
  • g: makes the regex find all matches rather than stopping after the first match.
  • m: makes the ^ and $ assertions match the start and end of each line within a string, rather than the start and end of the entire string.

Example

Let's consider this script, that matches all the email addresses in a multiline value, regardless of their case:

const message = `Hello,

Please contact us at Support@email.com for more details.

You can also reach out to helpdesk@company.dev.

Best,
The Team`;

const regex = /[a-zA-Z0-9._%+-]{1,64}@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/gi
const emails = message.match(regex) || [];

console.log(emails);

Which will produce this output:

[ 'Support@email.com', 'helpdesk@company.dev' ]

Summary

Here's a summary of what you've learned in this lesson:

  • A regular expression (or regex) is a pattern used to match character combinations in strings.
  • The /pattern/ expression or the RegExp class are used to create new regular expressions.
  • The regex.test() method is used to check whether a pattern exists in a string.
  • The string.match() method is used to list all the occurrences of a pattern in a string.
  • The . wildcard character matches any character within a pattern, except line terminators.
  • The [...] character class matches any individual character or character ranges within the brackets.
  • The [^...] character class excludes any character set from the match.
  • The | disjunction allows to specify alternative patterns.
  • The ^pattern boundary-type assertion matches from the beginning of the pattern.
  • The pattern$ boundary-type assertion matches from the end of the pattern.
  • The ? quantifier matches the preceding item 0 or 1 times.
  • The * quantifier matches the preceding item 0 or more times.
  • The + quantifier matches the preceding item 1 or more times.
  • The {n} quantifier matches the preceding item exactly n times.
  • The {n,} quantifier matches the preceding item at least n times.
  • The {n,m} quantifier matches the preceding item at least n times and at most m times.
  • The (pattern) capturing group stores the matched patterns into an array.
  • The (?:pattern) non-capturing group doesn't store the matched patterns.
  • The x(?=y) lookahead assertion matches "x" only if "x" is followed by "y".
  • The x(?!y) negative lookahead assertion matches "x" only if "x" is not followed by "y".
  • The (?<=y)x lookbehind assertion matches** "x" only if "x" is preceded by "y".
  • The (?<!y)x negative lookbehind assertion** matches "x" only if "x" is not preceded by "y".
  • The i flag makes the regex case-insensitive.
  • The g flag makes the regex find all matches.
  • The m flag makes the ^ and $ assertions match the start and end of each line within a string.

Enjoying the courses?

I've made these courses completely free so anyone can learn from them. If they've helped you and you'd like to actively support the work behind BackendBrewery, you can leave a tip:

Support BackendBrewery
Match Patterns With Regular Expressions in JavaScript | Backend Brewery