Patterns & flags
Learn JavaScript regular expressions from zero: create patterns, choose flags, read matches, and use test, exec, matchAll, and replace safely.
- 01Create regular expressionsChoose a literal or
new RegExp()and know when string escaping matters. - 02Read search resultsUse flags,
test(),exec(), match objects, groups, andindiceswithout being surprised bylastIndex. - 03Use string helpersApply regexes with
match,matchAll,replace,replaceAll,search, andsplitin practical text tasks.
Patterns for text
Strings answer exact questions: does this text include cat? Regular expressions answer pattern questions: is there a hashtag, a date-shaped piece, or a word no matter how it is capitalized? They are part of JavaScript, so every modern browser can use them with ordinary strings.
A regular expression, or regex, is a RegExp object that describes a pattern to search for in text. Flags are single-letter options that change how the search behaves.
A recipe card says what to look for and how to handle it. A regex is similar: the pattern lists the text shape, and the flags set options for the search.
- In real life: Ingredients to look for
- In JavaScript: Pattern pieces such as
cator a date shape - In real life: Cooking options
- In JavaScript: Flags such as
ifor ignore case orgfor global - In real life: Taste and adjust
- In JavaScript: Run
test,exec, or a string method and inspect the result
Where the analogy stops: A recipe produces food; a regex only describes text. It does not understand meaning, grammar, or nested languages unless your pattern says exactly how.
This lesson builds on strings, string methods, template literals, and Unicode & strings. We will use tiny character patterns like [A-Za-z] and \d, but the next lessons teach character classes, quantifiers, groups, anchors, and performance in depth.
Literals vs new RegExp()
STEP THROUGHJavaScript has two creation forms. A regex literal puts the pattern between slashes, like /cat/i. The constructor receives strings, like new RegExp("cat", "i"). Use a literal when the pattern is fixed. Use the constructor when the pattern or flags come from a variable.
Step through two ways to create a regular expression. The literal is fixed in the code; the constructor can use a variable.
script
const animal = "dog";const fromText = new RegExp(animal, "i"); console.log(literal.test("CAT"));console.log(fromText.test("hot dog"));console.log(fromText.source);Constructor strings have one extra rule: the JavaScript string parser sees backslashes before the regex parser does. A digit pattern is /\d+/ as a literal, but new RegExp("\\d+") as a constructor string.
new RegExp()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"));If text came from a learner, a search box, or an API, escape it before putting it in a regex. Modern browsers document RegExp.escape(userText) for this job; the Node 22 test runtime for this site does not have it yet, so the runnable example uses a small helper. The Character classes & sets lesson covers escaping and RegExp.escape in depth.
Flags change the search
8 FLAGSFlags sit after a literal, /cat/gi, or in the constructor’s second argument, new RegExp("cat", "gi"). Think of them as switches on the regex object.
| Flag | Name | First mental model |
|---|---|---|
g | Global | Find every match and update lastIndex between searches. |
i | Ignore case | Treat cat, CAT, and Cat as the same letters. |
m | Multiline | Let ^ and $ work at line breaks, not just the whole string edges. |
s | Dot all | Let . match line breaks too. |
u | Unicode | Read code points correctly, such as one emoji with ^.$. |
v | Unicode sets | Turns on newer Unicode set syntax; the next lesson covers set operations. |
y | Sticky | Match exactly at lastIndex, useful for tokenizers. |
d | Indices | Add an indices array showing [start, end] spans for matches and groups. |
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("-"));This is only the overview. m connects to anchors, v connects to Unicode set operations, and d becomes more powerful when you learn groups. Here you just need to recognize what each switch changes.
test(), exec(), and the lastIndex trap
STATEFULUse test() for a yes/no answer. Use exec() when you need details about the match. Both methods are simple until you reuse a regex with g or y. Those flags make the regex remember lastIndex, the next place to search.
Global and sticky regexes are stateful. Step through repeated test() calls and watch lastIndex change.
script
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));| Question | Good first choice | What to watch |
|---|---|---|
| Need a yes/no answer | regex.test(text) | Returns a boolean; beware g or y state on reused regexes. |
| Need the first match details | regex.exec(text) | Returns one match object with index, input, groups, and maybe indices. |
| Need simple string results | text.match(regex) | With g, returns matched strings. Without g, returns one match object. |
| Need all match objects | text.matchAll(regex) | Requires g and gives an iterator of match objects. |
| Need to rewrite text | replace / replaceAll | Use replacement patterns or a function replacer. |
The trap is not that g is bad. It is that a single regex object can carry state between calls. Reset lastIndex, create a fresh regex, or use a string method that manages the iteration for you.
Match objects and indices
exec() returns null when nothing matches. When it succeeds, it returns an array-like match object. match[0] is the full match, later entries are captured groups, index is the start position, and input is the original string.
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("-"));The named group and d flag are previews. Groups get their own lesson; this lesson only needs you to know that groups can name captured text and indices can show where the full match and captures live in the input.
String methods that accept regexes
SORT ITRegexes become practical through string methods. search returns an index. match returns simple matches or one detailed match. matchAll gives every detailed match. replace, replaceAll, and split transform text.
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"));Replacement patterns
Replacement strings have their own mini-language. $& means the whole match, $1 means the first captured group, $<name> means a named group, and a function replacer can compute the replacement in 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()));- 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`.- Wrap every search result in square brackets.
Return just `["#JS", "#web"]` from a short note.
Sort each task by the kind of result you need. The same pattern can be useful with different APIs.
Try patterns in a safe tester
PLAYGROUNDA tester helps you connect the pattern, flags, text, and match result. This one builds a RegExp inside try/catch, caps input length, refuses obvious slow nested quantifiers, and renders highlights as React text instead of HTML.
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(", "));#JavaScript9..20#regex25..31
2 matches. Indexes count from zero; end positions stop just after the match.
Try #[A-Za-z]+ with gi, then try an invalid pattern such as (. If you add d, the match list also shows index spans from the regex engine.
Practical uses
Regex basics show up in everyday UI work: quick form feedback, finding hashtags or codes, reformatting date strings with known shapes, and highlighting search terms. Keep validation honest: client-side regex is helpful feedback, not a trusted security boundary.
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"), "[$&]"));Regex is excellent for regular text shapes. Reach for a parser when the input is nested or language-like, such as full HTML, JavaScript, JSON with comments, or complex URLs. The performance lesson later covers catastrophic backtracking and ReDoS.
Common misconceptions
- “A regex understands meaning.” It only follows the pattern. It can find email-shaped text; it cannot prove the mailbox exists.
- “
gonly means find all.” It also makestest()andexec()advancelastIndexon the regex object. - “
matchandmatchAllare interchangeable.”match()changes shape withg;matchAll()gives detailed objects for every match and requiresg. - “The constructor uses the same escaping as a literal.” Constructor patterns are strings first, so backslashes often need another backslash.
- “Regex is always the shortest correct solution.” Sometimes
startsWith,includes,URL,Intl, or a parser is clearer and safer.
| Method | Returns | Best use |
|---|---|---|
test() | Boolean | Quick yes/no checks. |
exec() | One match object or null | Read index, groups, and indices for the next match. |
match() | Strings with g; one match object without g | Convenient simple matching. |
matchAll() | Iterator of match objects | Loop through every detailed match. |
Practice exercises
5 EXERCISESPredict the boolean printed by the snippet.
const answer = /complete/i.test("CompleteFrontend");
console.log(answer);The regex finds complete inside CompleteFrontend because i ignores case, so the console prints true.
lastIndex trapType all six printed values, separated by commas.
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);The outputs are true, 2, true, 5, false, 0: two matches, then a failed search from the end resets the regex.
Predict the exact joined output.
const text = "Ship #JS and #regex";
console.log([...text.matchAll(/#[A-Za-z]+/g)].map((match) => match[0] + "@" + match.index).join(" | "));#JS starts at index 5, and #regex starts at index 13, so the joined output is #JS@5 | #regex@13.
Type the reformatted date printed by the code.
const input = "2026-09-26";
console.log(input.replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1"));The regex captures 2026, 09, and 26. The replacement $3/$2/$1 prints 26/09/2026.
Predict the transformed text.
const userText = "a.b";
const escaped = userText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const pattern = new RegExp(escaped, "g");
console.log("a.b axb".replace(pattern, "[$&]"));Escaping turns a.b into a literal-dot pattern, so a.b is highlighted while axb is left alone.
Check your understanding
8 QUESTIONSQuestion 1 of 8Which statement best defines a regular expression?
Choose an answer to see the explanation.
Question 2 of 8What does the case-insensitive test print?
Read the code, then predictconst pattern = /cat/i; console.log(pattern.test("CAT"));Choose an answer to see the explanation.
Question 3 of 8Which constructor pattern safely looks for a digit?
Choose an answer to see the explanation.
Question 4 of 8What is the
g-flag trap in this code?Read the code, then predictconst 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.
Question 5 of 8What does the
dflag add to a match object?Choose an answer to see the explanation.
Question 6 of 8What do the hashtag matches print?
Read the code, then predictconst text = "Tags: #JS #web"; console.log([...text.matchAll(/#[A-Za-z]+/g)].map((m) => m[0]).join(","));Choose an answer to see the explanation.
Question 7 of 8Which replacement pattern means the whole match?
Choose an answer to see the explanation.
Question 8 of 8What does the date replacement print?
Read the code, then predictconsole.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
RegExppattern object used with strings. - Use literals for fixed patterns and
new RegExp()for variable patterns. - Flags change behavior;
gandyalso makelastIndexmatter. exec()andmatchAll()expose match objects;dadds index spans.- Choose the string method that matches the result you need: find, list, split, or replace.
Remember the one-liner.
A regex is a compact text pattern plus flags; use it when a pattern is clearer than hand-written string checks.
Up next: Character classes & sets.