JavaScript Core/
Lesson

Regular expressions show up everywhere: form validation, log parsing, search-and-replace, URL routing, syntax highlighting. When AI generates code that processes text, there's a good chance it reaches for a regexWhat is regex?A compact pattern language for matching, searching, and replacing text, built into nearly every programming language and code editor.. The problem is that regex has a reputation for being unreadable, and AI-generated regex is often more complex than it needs to be.

You don't need to become a regex wizard. You need to read regex patterns when you encounter them, write simple ones, and know when a regex is overkill.

Creating a regexWhat is regex?A compact pattern language for matching, searching, and replacing text, built into nearly every programming language and code editor. in JavaScript

Two ways to create a regex. The literal syntax is the one you'll see 99% of the time:

// Literal — most common
const pattern = /hello/;

// Constructor — useful when pattern is dynamic
const dynamic = new RegExp('hello');
const fromVar = new RegExp(userInput, 'i');  // pattern from a variable

Use the literal /pattern/ for hardcoded patterns. Use new RegExp() when the pattern comes from a variable — like a user's search query.

AI pitfall
AI sometimes uses new RegExp() for static patterns, adding unnecessary complexity. If the pattern is hardcoded, use the literal syntax. It's shorter and the engine can optimize it at parse time.
02

Flags

Flags go after the closing slash and change how the pattern behaves:

FlagNameWhat it does
gGlobalFind all matches, not just the first
iCase-insensitive/hello/i matches "Hello", "HELLO", "hElLo"
mMultiline^ and $ match start/end of each line, not just the string
sDotAll. matches newline characters too
uUnicodeProper Unicode support (emoji, accented chars)
'Hello hello HELLO'.match(/hello/gi);
// ['Hello', 'hello', 'HELLO']

'Hello hello'.match(/hello/);
// ['hello'] — only first match, case-sensitive
03

Character classes — matching types of characters

Instead of matching exact characters, you can match categories:

PatternMatchesOpposite
\dAny digit (0-9)\D — anything except digits
\wWord character (a-z, A-Z, 0-9, _)\W — non-word characters
\sWhitespace (space, tab, newline)\S — non-whitespace
.Any character except newline
[abc]Any of a, b, or c[^abc] — anything except a, b, c
[a-z]Any lowercase letter[^a-z] — not a lowercase letter
// Match a phone number like 06 12 34 56 78
/\d{2}\s\d{2}\s\d{2}\s\d{2}\s\d{2}/.test('06 12 34 56 78');
// true
04

Quantifiers — how many times

Quantifiers follow a character or group and say "match this N times":

QuantifierMeaningExample
+1 or more\d+ matches "1", "42", "99999"
*0 or more\d* matches "" or "123"
?0 or 1 (optional)https? matches "http" and "https"
{3}Exactly 3\d{3} matches "123" but not "12"
{2,4}Between 2 and 4\d{2,4} matches "12", "123", "1234"
{2,}2 or more\d{2,} matches "12" and beyond
// Match an email-like pattern (simplified)
/\w+@\w+\.\w+/.test('alice@example.com');
// true
Good to know
Quantifiers are greedy by default — they match as much as possible. Add ? after a quantifier to make it lazy (match as little as possible). This matters when parsing HTML or extracting quoted strings: /".*"/ grabs everything between the first and last quote, /".*?"/ grabs the shortest match.
05

Anchors — where to match

Anchors don't match characters. They match positions:

AnchorMatches
^Start of string (or line with m flag)
$End of string (or line with m flag)
\bWord boundary
/^hello$/.test('hello');        // true — exact match
/^hello$/.test('hello world');  // false — has more text after
/\bworld\b/.test('hello world'); // true — "world" as a whole word
/\bworld\b/.test('helloworld'); // false — no word boundary
06

Groups and captures

Parentheses () create capture groups — they extract parts of a match:

const datePattern = /(\d{4})-(\d{2})-(\d{2})/;
const match = '2025-03-15'.match(datePattern);

match[0];  // '2025-03-15' — full match
match[1];  // '2025' — first group (year)
match[2];  // '03' — second group (month)
match[3];  // '15' — third group (day)

Named groups (ES2018)

Numbered groups get confusing fast. Named groups make your intent clear:

const datePattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = '2025-03-15'.match(datePattern);

match.groups.year;   // '2025'
match.groups.month;  // '03'
match.groups.day;    // '15'

Named groups work in replace() too:

// Reformat date from YYYY-MM-DD to DD/MM/YYYY
'2025-03-15'.replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  '__CODE_BLOCK_2__lt;day>/__CODE_BLOCK_2__lt;month>/__CODE_BLOCK_2__lt;year>'
);
// '15/03/2025'
lt;day>/
// Reformat date from YYYY-MM-DD to DD/MM/YYYY
'2025-03-15'.replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  '__CODE_BLOCK_2__lt;day>/__CODE_BLOCK_2__lt;month>/__CODE_BLOCK_2__lt;year>'
);
// '15/03/2025'
lt;month>/
// Reformat date from YYYY-MM-DD to DD/MM/YYYY
'2025-03-15'.replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  '__CODE_BLOCK_2__lt;day>/__CODE_BLOCK_2__lt;month>/__CODE_BLOCK_2__lt;year>'
);
// '15/03/2025'
lt;year>' ); // '15/03/2025'" data-fullscreen-lang="javascript" class="absolute top-2 right-8 p-1 rounded bg-gray-700 hover:bg-gray-600 text-gray-400 hover:text-gray-200 transition-colors opacity-0 group-hover/code:opacity-100 z-10" title="Fullscreen">
// Reformat date from YYYY-MM-DD to DD/MM/YYYY
'2025-03-15'.replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  '__CODE_BLOCK_2__lt;day>/__CODE_BLOCK_2__lt;month>/__CODE_BLOCK_2__lt;year>'
);
// '15/03/2025'
lt;day>/
// Reformat date from YYYY-MM-DD to DD/MM/YYYY
'2025-03-15'.replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  '__CODE_BLOCK_2__lt;day>/__CODE_BLOCK_2__lt;month>/__CODE_BLOCK_2__lt;year>'
);
// '15/03/2025'
lt;month>/
// Reformat date from YYYY-MM-DD to DD/MM/YYYY
'2025-03-15'.replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  '__CODE_BLOCK_2__lt;day>/__CODE_BLOCK_2__lt;month>/__CODE_BLOCK_2__lt;year>'
);
// '15/03/2025'
lt;year>' ); // '15/03/2025'" class="absolute top-2 right-2 p-1 rounded bg-gray-700 hover:bg-gray-600 text-gray-400 hover:text-gray-200 transition-colors opacity-0 group-hover/code:opacity-100 z-10" title="Copy">
// Reformat date from YYYY-MM-DD to DD/MM/YYYY
'2025-03-15'.replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  '
// Reformat date from YYYY-MM-DD to DD/MM/YYYY
'2025-03-15'.replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  '__CODE_BLOCK_2__lt;day>/__CODE_BLOCK_2__lt;month>/__CODE_BLOCK_2__lt;year>'
);
// '15/03/2025'
lt;day>/
// Reformat date from YYYY-MM-DD to DD/MM/YYYY
'2025-03-15'.replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  '__CODE_BLOCK_2__lt;day>/__CODE_BLOCK_2__lt;month>/__CODE_BLOCK_2__lt;year>'
);
// '15/03/2025'
lt;month>/
// Reformat date from YYYY-MM-DD to DD/MM/YYYY
'2025-03-15'.replace(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
  '__CODE_BLOCK_2__lt;day>/__CODE_BLOCK_2__lt;month>/__CODE_BLOCK_2__lt;year>'
);
// '15/03/2025'
lt;year>'
); // '15/03/2025'
AI pitfall
AI often generates regex with numbered groups like CALLOUT, $2, $3. Named groups
AI pitfall
AI often generates regex with numbered groups like CALLOUT, $2, $3. Named groups __CALLOUT_3__lt;name> are almost always better — they survive refactoring and make the pattern self-documenting.
lt;name>
are almost always better — they survive refactoring and make the pattern self-documenting.
07

The methods you'll actually use

JavaScript has several regexWhat is regex?A compact pattern language for matching, searching, and replacing text, built into nearly every programming language and code editor. methods. These four cover virtually every use case:

MethodReturnsUse when
regex.test(str)true / falseYou just need yes/no
str.match(regex)Array of matches or nullYou need the matched text
str.replace(regex, replacement)New stringYou need to transform text
str.split(regex)Array of partsYou need to break text apart
// Validation
/^\d{5}$/.test('75001');                    // true — French postal code

// Extraction
'Call 06-12-34 or 07-56-78'.match(/\d{2}-\d{2}-\d{2}/g);
// ['06-12-34', '07-56-78']

// Replacement
'Hello   World'.replace(/\s+/g, ' ');       // 'Hello World'

// Splitting
'one, two,  three'.split(/,\s*/);            // ['one', 'two', 'three']
08

When NOT to use regexWhat is regex?A compact pattern language for matching, searching, and replacing text, built into nearly every programming language and code editor.

This is just as important as knowing regex syntax. AI reaches for regex when simpler methods exist:

TaskUse thisNot this
Check if string contains textstr.includes('text')/text/.test(str)
Check if string starts withstr.startsWith('http')/^http/.test(str)
Check if string ends withstr.endsWith('.json')/\.json$/.test(str)
Simple splitstr.split(',')str.split(/,/)
Simple replacestr.replaceAll('a', 'b')str.replace(/a/g, 'b')

Use regex when the pattern involves variability — digits, optional parts, alternatives, repetition. If you're matching a fixed string, you don't need regex.

09

Quick reference

PatternMatchesExample
\dDigit\d+ → "42"
\wWord char\w+ → "hello_123"
\sWhitespace\s+ → " "
.Any chara.c → "abc", "a1c"
[aeiou]Any vowel[aeiou] → "a"
^...$Exact match^\d{5}$ → "75001"
(...)Capture group(\d+)-(\d+)
(?<n>...)Named group(?<year>\d{4})
a\|bAlternativecat\|dog → "cat" or "dog"
(?:...)Non-capturing groupGroups without capturing