completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Patterns & flags

Learn JavaScript regular expressions from zero: create patterns, choose flags, read matches, and use test, exec, matchAll, and replace safely.

By the end, you can
  • 01
    Make a regexWrite a regex literal or use new RegExp(), and know when a string needs a second backslash.
  • 02
    Read what a search findsUse flags, test(), exec(), and match objects, and know why lastIndex can change the answer.
  • 03
    Use regexes with stringsFind, list, cut, and change text with search, match, matchAll, split, replace, and replaceAll.

Patterns for text

A string method such as includes() looks for exact text. It can answer: does this text contain cat? Sometimes you want to find a kind of text instead. For example:

  • any hashtag, such as #JS or #web,
  • anything shaped like a date, such as 2026-09-26,
  • the word cat, written with capital or small letters.

A regular expression can find all of these. It is built into JavaScript, so it works in every modern browser.

Definition

A regular expression, or regex for short, is a pattern that describes the text you want to find. In JavaScript, a regex is an object of type RegExp. Flags are single letters added to a regex. Each flag turns on one option for the search.

The regex /cat/i split into two parts. Part 1, /cat/, is the pattern: what to find. Part 2, i, is the flags: how to search./cat/i1. patternwhat to find2. flagshow to search
Between the two slashes is the pattern, the text to look for. After the last slash come the flags; i means "ignore capital and small letters".
Real-life analogyA recipe card for finding text

A recipe card lists what goes in the dish. It also has notes on how to cook it. You follow the card, then taste the food to check it.

In real life: The list of what goes in
In JavaScript: The pattern: the text to look for, such as cat
In real life: Notes such as "bake slowly"
In JavaScript: The flags, such as i (ignore case) or g (find every match)
In real life: You taste the food
In JavaScript: You run test() or a string method and look at the result

This lesson uses what you know about strings, string methods, template literals, and Unicode & strings. It uses a few small pattern pieces, such as [A-Za-z] (any one letter) and \d (any one digit). The lessons after this one explain each kind of piece in full.

Two ways to make a regex

STEP THROUGH

There are two ways to make a regex:

  • A regex literal writes the pattern between two slashes, like /cat/i. The flags go after the last slash.
  • The constructor, new RegExp(), builds a regex from strings, like new RegExp("cat", "i"). The first string is the pattern. The second string holds the flags.

Use a literal when you know the pattern while you write the code. Use new RegExp() when the pattern or the flags are stored in a variable.

The code below uses two tools. test(text) returns true if the pattern is found in the text, and false if not. .source gives the pattern as text, without the slashes or flags.

Step through: a literal and new RegExp()
Step 0 of 6Ready
Your turn: follow the blue line

Step through two ways to make a regex. The literal is written in the code. new RegExp() builds one from the text in a variable.

Running in
  1. script
Next: line 1
const animal = "dog";const fromText = new RegExp(animal, "i"); console.log(literal.test("CAT"));console.log(fromText.test("hot dog"));console.log(fromText.source);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
A box called animal holds the string dog. An arrow labelled new RegExp(animal, i) leads to a box called fromText, which holds the regex /dog/i."dog"animalnew RegExp(animal, "i")/dog/ifromText
new RegExp() reads the text in animal and builds the regex /dog/i from it. If animal held different text, you would get a different regex.

Strings for new RegExp() have one extra rule. Inside a JavaScript string, a backslash \ starts a special code, such as \n for a line break. JavaScript reads the string first and uses up single backslashes. Only then does the regex see the text. So for \d+ (one or more digits), write two backslashes in the string:

  • As a literal: /\d+/
  • With the constructor: new RegExp("\\d+")

Text that comes from a user brings a second problem. In a pattern, some characters have a special meaning. A dot . means "any one character". To escape a character means to put a backslash in front of it, so it stands for itself. Then \. means a real dot.

Make user text safe before new RegExp()Pop out in the code editor (opens in a new tab)JavaScript
const userText = "a.b";const unsafe = new RegExp(userText);const safe = new RegExp(userText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")); console.log(unsafe.test("axb"));console.log(safe.test("axb"));console.log(safe.test("a.b"));
  • Line 2 builds a regex straight from "a.b". The dot means any character, so line 5 finds axb and prints true.
  • Line 3 first puts a backslash in front of every special character. Now the dot means a real dot.
  • So line 6 prints false for axb, and line 7 prints true for a.b.
Escape text that comes from users

Text can come from a person typing, a search box, or a server. Escape it before you put it in a regex. Newer browsers have RegExp.escape(userText) for this. The Node 22 version that runs this site's tests doesn't have it yet, so the example uses a short replace() instead. The Character classes & sets lesson explains escaping and RegExp.escape in full.

Flags change the search

8 FLAGS

With a literal, flags go right after the last slash: /cat/gi. With the constructor, they go in the second string: new RegExp("cat", "gi"). Each flag turns one option on. The order of the letters doesn't matter.

Each row of this table is one flag: its letter, its name, and what it changes.

The eight JavaScript regex flags
FlagIts nameWhat it changes
gGlobalIt finds every match, not just the first. It also makes the regex remember lastIndex, the index where the next search starts.
iIgnore caseIt treats capital and small letters as the same, so cat, CAT, and Cat all match.
mMultilineIt lets ^ (start) and $ (end) match at the start and end of every line, not only of the whole text.
sDot allIt lets . (any one character) match a line break too.
uUnicodeIt reads each emoji and other special characters as one character, so ^.$ matches one emoji.
vUnicode setsIt is a newer form of u that also allows set rules inside [ ]. The next lesson covers them.
yStickyIt only matches exactly at lastIndex, not anywhere after it. This helps when you read text one piece at a time.
dIndicesIt adds an indices list with the [start, end] position of the match and of each group.
One small example for each flagPop out in the code editor (opens in a new tab)JavaScript
console.log(/cat/g.test("cat cat"));console.log(/cat/i.test("CAT"));console.log(/^cat/m.test("dog\ncat"));console.log(/a.b/s.test("a\nb"));console.log(/^.$/u.test("😀"));console.log(/^[\p{Letter}&&[A-F]]$/v.test("D"));const sticky = /cat/y;sticky.lastIndex = 4;console.log(sticky.test("one cat"));const withIndices = /ID-(\d+)/d.exec("ID-42");console.log(withIndices.indices[1].join("-"));

Every line prints true, except the last one, which prints 3-5. A few lines need a word:

  • Line 3: \n is a line break, so the text has two lines. With m, ^ can match the start of the second line.
  • Line 5: ^.$ means "exactly one character". With u, one emoji counts as one character.
  • Line 6: the v flag lets the pattern ask "is this a letter, and also one of A to F?"
  • Lines 7 to 9: the sticky regex starts at index 4, where cat begins in "one cat".
  • Lines 10 and 11: with d, JavaScript records where the digits 42 sit: from index 3 up to 5.

You don't need every flag yet. Later lessons use m with anchors (^ and $), v with character sets, and d with groups. For now, know what each letter turns on.

test(), exec(), and lastIndex

STATEFUL

A regex has two methods that search text:

  • test(text) answers yes or no. It returns true or false.
  • exec(text) returns details about the first match: the matched text, where it starts, and more. If nothing matches, it returns null.

Both are simple, until the regex has the g or y flag. Then the regex keeps a number called lastIndex: the index where the next search starts. An index is a position in a string, counting from 0. Each search that finds a match moves lastIndex to just after the match. Step through the code and watch lastIndex change.

Step through: the g flag moves lastIndex
Step 0 of 10Ready
Your turn: follow the blue line

A regex with g or y remembers where it stopped. Step through the test() calls and watch lastIndex change.

Running in
  1. script
Next: line 1
const text = "cat cat"; console.log(word.test(text));console.log(word.lastIndex);console.log(word.test(text));console.log(word.lastIndex);console.log(word.test(text));console.log(word.lastIndex);word.lastIndex = 0;console.log(word.test(text));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
The text "cat cat" as seven cards, indexes 0 to 6. Match 1 is indexes 0 to 2, match 2 is indexes 4 to 6. A tag that says lastIndex 3 points at index 3, the space, where the next test starts.0c1a2t34c5a6tmatch 1match 2lastIndex3next test starts here
The first word.test(text) finds cat at indexes 0 to 2. The regex then keeps lastIndex = 3, so the next test starts at the space.

The same thing happens with a different string. lastIndex belongs to the regex, not to the text.

One g regex used on two stringsPop out in the code editor (opens in a new tab)JavaScript
const word = /cat/g; console.log(word.test("cat"));console.log(word.test("cat"));word.lastIndex = 0;console.log(word.test("cat"));
  • Line 3 finds cat and prints true. Now lastIndex is 3.
  • Line 4 passes a new string, also "cat". But the search starts at index 3, the end of the text. So it prints false, and the failed search sets lastIndex back to 0.
  • Line 5 sets lastIndex to 0 by hand, to make the fresh start clear. Line 6 prints true.

This table helps you pick a method. Start from what you need in the first column.

Which regex method to use for each job
What you needMethod to try firstWhat to watch for
You need a yes or no answer.regex.test(text)It returns true or false. With g or y, a reused regex remembers lastIndex.
You need details about the first match.regex.exec(text)It returns one match object with index, input, groups, and sometimes indices. It returns null if nothing matches.
You need only the matched text.text.match(regex)With g, it returns an array of matched strings. Without g, it returns one match object.
You need details for every match.text.matchAll(regex)The regex must have g. It gives one match object per match, for a loop.
You need to change the text.replace or replaceAllUse special codes such as $1 in the replacement, or pass a function that builds the new text.

The g flag is not bad. The problem is that one regex object keeps lastIndex between calls. To avoid surprises, do one of these:

  • Set lastIndex = 0 before a new search.
  • Make a new regex inside the function that does the search.
  • Use match() or replaceAll() with a g regex. They always start from index 0.

What exec() gives back

When exec() finds a match, it returns a match object. A match object is an array with a few extra properties:

  • match[0] is the whole matched text.
  • match[1], match[2], and so on hold the captured groups. A captured group is a part of the pattern inside round brackets ( ). The text it matched is saved on its own.
  • match.index is the index where the match starts.
  • match.input is the whole string that was searched.
  • match.groups holds groups that have names.
  • match.indices holds start and end positions. It only exists with the d flag.

In the code below, (?<tag>...) is a named group: a captured group with a name, here tag. Inside it, #[A-Za-z]+ means a # and then one or more letters.

Reading a match objectPop out in the code editor (opens in a new tab)JavaScript
const pattern = /(?<tag>#[A-Za-z]+)/d;const text = "Use #JS today";const match = pattern.exec(text); console.log(match[0]);console.log(match.index);console.log(match.input === text);console.log(match.groups.tag);console.log(match.indices[0].join("-"));console.log(match.indices.groups.tag.join("-"));
  • Line 5 prints #JS, the whole match.
  • Line 6 prints 4, because # is at index 4 in "Use #JS today".
  • Line 7 prints true: input is the same string as text.
  • Line 8 prints the text of the group named tag: #JS.
  • Lines 9 and 10 print 4-7. The match starts at index 4 and ends just before index 7.

Groups get their own lesson later. For now, know two things. groups gives names to parts of a match. indices tells you where each part sits in the text.

String methods that accept regexes

SORT IT

Some string methods take a regex. Each one gives back a different kind of result:

  • search() gives the index of the first match, or -1 if there is none.
  • match() gives an array of matched strings with g, or one match object without g.
  • matchAll() gives a match object for every match.
  • replace() and replaceAll() give a new string with the matches changed.
  • split() cuts the string at each match and gives an array of the pieces.
Six string methods, one regex eachPop out in the code editor (opens in a new tab)JavaScript
const text = "Order 2026-09-26: chai, cake. Tags: #JS #web"; console.log(text.search(/chai/i));console.log(text.match(/#[A-Za-z]+/g).join(", "));console.log([...text.matchAll(/#[A-Za-z]+/g)].map((m) => `${m[0]}@${m.index}`).join(" | "));console.log("red, green; blue".split(/,\s*|;\s*/).join("|"));console.log(text.replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1"));console.log("cat cat".replaceAll(/cat/g, "dog"));
  • Line 3 prints 18, the index where chai starts.
  • Line 4 prints both hashtags. The pattern #[A-Za-z]+ means a # and then one or more letters.
  • Line 5 prints each hashtag with its index.
  • Line 6 cuts the text at a comma or a semicolon. In the pattern, | means "or", and \s* takes any spaces after the mark.
  • Line 7 turns the date around (the next part explains $3/$2/$1).
  • Line 8 changes every cat to dog.
Three string methods and what they give back. text.search(/chai/i) gives the number 18. text.match(/#[A-Za-z]+/g) gives the array "#JS", "#web". "cat cat".replaceAll(/cat/g, "dog") gives the new string "dog dog".text.search(/chai/i)18text.match(/#[A-Za-z]+/g)["#JS", "#web"]"cat cat".replaceAll(/cat/g, "dog")"dog dog"
Each string method gives back a different kind of result. search gives a number, match with g gives an array of strings, and replaceAll gives a new string.

Special codes in the replacement text

The second argument of replace() is the replacement: the text that goes in place of each match. Inside it, a few codes have a special meaning:

  • $& puts in the whole match.
  • $1 puts in the first captured group, $2 the second, and so on.
  • $<name> puts in the group with that name.

You can also pass a function instead of a string. JavaScript calls the function once for each match. The function returns the new text.

Four ways to write the replacementPop out in the code editor (opens in a new tab)JavaScript
console.log("cat".replace(/cat/, "[$&]"));console.log("Ada Lovelace".replace(/([A-Za-z]+) ([A-Za-z]+)/, "$2, $1"));console.log("#javascript".replace(/#(?<name>[a-z]+)/, "topic:$<name>"));console.log("fee fi".replace(/f[a-z]+/g, (match) => match.toUpperCase()));
  • Line 1 prints [cat]: $& puts the whole match inside the square brackets.
  • Line 2 prints Lovelace, Ada: the two groups swap places.
  • Line 3 prints topic:javascript: the group named name holds javascript.
  • Line 4 prints FEE FI: the function turns each match into capital letters.
Choose the regex method
  • Does this form field look like an email address?
  • Find the first hashtag and where it starts.
  • List every hashtag with its index.
  • Turn `2026-09-26` into `26/09/2026`.
  • Put square brackets around every search result.
  • Return just `["#JS", "#web"]` from a short note.
Try it yourself
0 of 6 correct

Put each task under the kind of result it needs. One pattern can be used with different methods.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Try patterns in a safe tester

PLAYGROUND

The tester below shows a pattern, its flags, some text, and the matches together. Type the pattern without the slashes. To stay safe, the tester:

  • builds the regex inside try/catch, so a broken pattern shows an error message instead of breaking the page,
  • limits how long the pattern and the text can be,
  • refuses a repeat inside a repeat, such as (a+)+, because it can run very slowly,
  • shows the matches as plain text, never as HTML.
Live regex tester
How the tester collects matchesPop out in the code editor (opens in a new tab)JavaScript
const pattern = new RegExp("#[A-Za-z]+", "gi");const text = "Learning #JavaScript and #regex";const matches = [...text.matchAll(pattern)].map((match) => ({  text: match[0],  index: match.index,})); console.log(matches.map((match) => `${match.text}@${match.index}`).join(", "));
Matches2 found
Learning #JavaScript and #regex with JS.
  1. #JavaScript 9..20
  2. #regex 25..31
Try it yourself

2 matches. Indexes count from 0. Each end position is just after the last matched character.

The pattern and the text have length limits, and the regex is built inside try/catch. The tester refuses a repeat inside a repeat, which can run very slowly.

Try #[A-Za-z]+ with the flags gi. Then try a broken pattern, such as a single (. If you add the d flag, each match in the list also shows its d positions.

Practical uses

Regexes show up in everyday web pages. You can use them to:

  • give quick feedback while someone fills in a form,
  • find hashtags or codes in text,
  • change the format of dates that always have the same shape,
  • highlight the words someone searched for.

A regex check in the browser only helps the user. It doesn't protect anything, because the user can change code in their own browser. The server must check the data again.

Four everyday regex tasksPop out in the code editor (opens in a new tab)JavaScript
const email = "ada@example.com";const text = "Learning #JavaScript and #regex on 2026-09-26";const term = "js"; console.log(/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email));console.log(text.match(/#[A-Za-z]+/g).join(", "));console.log(text.replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1"));console.log("JS regex basics".replace(new RegExp(term, "gi"), "[$&]"));
  • Line 5 checks that the email has the shape "text, @, text, dot, text". It prints true.
  • Line 6 prints both hashtags.
  • Line 7 changes the date to 26/09/2026.
  • Line 8 builds a regex from term and wraps each js, in any case, in square brackets.
Phone, postcode, spaces, and hashtagsPop out in the code editor (opens in a new tab)JavaScript
const phone = "555-123-4567";const postcode = "SW1A 1AA";const messy = "  Asha   likes   tea  ";const note = "New #JS lesson, #regex today"; console.log(/^\d{3}-\d{3}-\d{4}$/.test(phone));console.log(/^[A-Z]{1,2}\d[A-Z\d]? \d[A-Z]{2}$/i.test(postcode));console.log(messy.replace(/\s+/g, " ").trim());console.log(note.match(/#[A-Za-z0-9_]+/g).join(", ")); const cards = "<p>one</p><p>two</p>";console.log(cards.match(/<p>.*<\/p>/)[0]);console.log(cards.match(/<p>.*?<\/p>/)[0]);
  • Line 6 checks a phone number shape: three digits, a dash, three digits, a dash, four digits.
  • Line 7 checks a UK postcode shape, with a space in the middle.
  • Line 8 turns each run of spaces into one space. \s+ means one or more spaces. Then trim() removes the spaces at both ends.
  • Line 9 collects every hashtag with match() and g.
How much a pattern takes

A character class such as [A-Za-z] matches one character from a set: here, any letter. A quantifier says how many: \d{3} means exactly three digits. Quantifiers are greedy at first: .* takes as many characters as it can. Adding ? makes it lazy: .*? takes as few as it can. The Character classes and Quantifiers lessons explain more.

Lines 12 and 13 show the difference. The greedy pattern on line 12 takes both paragraphs as one match: <p>one</p><p>two</p>. The lazy pattern on line 13 stops at the first </p>, so it gives <p>one</p>. Choose the narrowest pattern that fits your data. It is easier to read, and it is less likely to match text you didn't expect.

When to use something else

A regex works best for text with a simple shape. Some text has parts inside parts, such as full HTML, JavaScript, JSON with comments, or long URLs. For that, use a parser: a tool that reads the whole structure, not just a pattern. A later lesson on regex speed shows patterns that can freeze a page.

Common misconceptions

  • "A regex understands what text means." It only follows the pattern. It can find text shaped like an email address. It can't tell whether that mailbox exists.
  • "g only means find every match." It also makes test() and exec() move lastIndex on the regex object.
  • "match() and matchAll() give the same thing." match() gives different results with and without g. matchAll() always gives one match object per match, and it needs g.
  • "new RegExp() uses the same backslashes as a literal." Its pattern is a string first, so each backslash usually needs a second backslash.
  • "A regex is always the shortest correct answer." Sometimes startsWith(), includes(), URL, Intl, or a parser is clearer and safer.

These four methods look alike. Each row says what one of them gives back and when to use it.

Four regex methods and what each gives back
MethodWhat it gives backWhen to use it
test()true or false.Use it for a quick yes or no check.
exec()One match object, or null if nothing matches.Use it to read index, groups, and indices of the next match.
match()With g, an array of matched strings. Without g, one match object.Use it when you only need the matched text.
matchAll()One match object for every match, ready for a for...of loop.Use it when you need details for every match.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upCase-insensitive test

Read the code. What does it print: true or false?

Starter codePop out in the code editor (opens in a new tab)JavaScript
const answer = /complete/i.test("CompleteFrontend");
console.log(answer);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeFollow lastIndex step by step

    Type all six printed values, with commas between them.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const word = /js/g;
    const text = "js js";
    console.log(word.test(text));
    console.log(word.lastIndex);
    console.log(word.test(text));
    console.log(word.lastIndex);
    console.log(word.test(text));
    console.log(word.lastIndex);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeList every hashtag with an index

      What exact text does the code print?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const text = "Ship #JS and #regex";
      console.log([...text.matchAll(/#[A-Za-z]+/g)].map((match) => match[0] + "@" + match.index).join(" | "));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeChange the date format

        Type the date the code prints.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const input = "2026-09-26";
        console.log(input.replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1"));

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeEscape a search term before highlighting

          What text does the code print?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const userText = "a.b";
          const escaped = userText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
          const pattern = new RegExp(escaped, "g");
          console.log("a.b axb".replace(pattern, "[$&]"));

          Answer, then press Check. Spacing and letter case don’t matter.

            Check your understanding

            8 QUESTIONS
            Lesson quiz · 8 questionsScore: first tries count
            1. Question 1 of 8What is a regular expression?

              Choose an answer to see the explanation.

            2. Question 2 of 8What does this test with the i flag print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const pattern = /cat/i;
              console.log(pattern.test("CAT"));

              Choose an answer to see the explanation.

            3. Question 3 of 8You build a digit pattern from a string. Which call is right?

              Choose an answer to see the explanation.

            4. Question 4 of 8Why does the third call print false?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const word = /js/g;
              console.log(word.test("js js"));
              console.log(word.test("js js"));
              console.log(word.test("js js"));

              Choose an answer to see the explanation.

            5. Question 5 of 8What does the d flag add to a match object?

              Choose an answer to see the explanation.

            6. Question 6 of 8What do the hashtag matches print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const text = "Tags: #JS #web";
              console.log([...text.matchAll(/#[A-Za-z]+/g)].map((m) => m[0]).join(","));

              Choose an answer to see the explanation.

            7. Question 7 of 8Which code in the replacement means the whole match?

              Choose an answer to see the explanation.

            8. Question 8 of 8What does the date replacement print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log("2026-09-26".replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1"));

              Choose an answer to see the explanation.

            Key takeaways

            • A regex is a RegExp object: a pattern you use to search strings.
            • Use a literal for a pattern you know while writing the code. Use new RegExp() when the pattern is in a variable.
            • Flags turn options on. With g or y, the regex also remembers lastIndex.
            • exec() and matchAll() give match objects. The d flag adds start and end positions.
            • Pick the string method that gives the result you need: an index, a list, pieces, or new text.

            Remember the one-liner.
            A regex is a short text pattern plus flags. Use it when a pattern is clearer than checking the text by hand.

            Up next: Character classes & sets.

            CompleteFrontend Clear concepts. Working examples.