← Back to blog

Interactive Introduction to Regular Expressions in JavaScript

Regular expressions are patterns that describe text. They look cryptic at first, but once you understand the building blocks, they become surprisingly intuitive. In this post, I'm going to walk through how regex works, step by step, with interactive examples you can play with along the way.

By the end, you'll be able to match patterns, validate input, and transform text. Let's start with something simple.

Matching text

At its core, a regular expression just asks one question: "Does this pattern exist in my text?"

The pattern /hello/ matches the exact sequence of characters h-e-l-l-o. Nothing fancy. It either finds that sequence or it doesn't.

Try changing the pattern below. See which strings match and which don't.

Your first pattern

Change the pattern and watch the results

//
hello worldmatch
say hellomatch
hi thereno match
HELLOno match
helloooomatch

A few things to notice here. First, "HELLO" doesn't match because regex is case-sensitive by default. Second, "helloooo" does match because the pattern only needs to exist somewhere in the string. It doesn't have to match the whole thing.

What if we want to match regardless of case? We add a flag. Try typing i in the flags field below.

Case insensitivity

Add 'i' to the flags field

//
Hello Worldno match
HELLO!no match
hellomatch
hino match
HeLLono match

The i flag tells the regex engine to ignore case. Flags go after the closing slash and modify how the whole pattern behaves. You'll see more flags later.

Character classes

So far we've only matched exact text. But what if you want to match any digit? Or any letter? That's where character classes come in.

Instead of matching a specific character, character classes match a category of characters. Click through the buttons below to see what each one matches.

Character classes

Click a character class to see what it matches

\dAny digit (0-9)
Equivalent to: [0-9]
Matches highlighted
Hello World 123!

These shortcuts are way cleaner than spelling out every possibility. Writing \d is much nicer than [0123456789].

You can also create your own character sets using square brackets. The pattern [aeiou] matches any vowel. The pattern [a-z] matches any lowercase letter. The hyphen creates a range.

Custom character sets

Edit the pattern and test string to see matches

//
Result (3 matches)
Hello World
Matches found:
[0]"e"at index 1
[1]"o"at index 4
[2]"o"at index 7

Want to match everything except certain characters? Put a ^ inside the brackets. The pattern [^0-9] matches any character that is not a digit.

Quantifiers

Here's where regex starts to get powerful. What if you want to match multiple digits in a row?

You could write \d\d\d to match exactly three digits. But that's clunky. What if you don't know how many digits there will be?

Quantifiers let you specify how many times a pattern should repeat. Play with the buttons below to see each one in action.

Quantifiers

See how quantifiers control repetition

Pattern: /go+l/
Matches "go" followed by one or more "o"s, then "l"
Result (1 match)
goooooal! go! gol goal

Here's what each quantifier means:

QuantifierMeaning
+One or more
*Zero or more
?Zero or one (optional)
{n}Exactly n times
{n,m}Between n and m times

The difference between + and * is subtle but important. The plus sign requires at least one match. The asterisk allows zero matches, which means it can match "nothing" and still succeed.

A practical example

Let's put this together with a real example. Say you want to validate phone numbers in the format XXX-XXX-XXXX.

Your first attempt might look like this:

/\d\d\d-\d\d\d-\d\d\d\d/

It works, but all those \ds make it hard to see what's going on. We can use quantifiers to clean it up:

/\d{3}-\d{3}-\d{4}/

Much better. But what about phone numbers without hyphens? We can make the hyphens optional using ?:

/\d{3}-?\d{3}-?\d{4}/

Watch how each pattern handles different phone number formats:

Building a phone pattern

Watch how different patterns validate phone numbers

Current Pattern
/\d\d\d-\d\d\d-\d\d\d\d/
Phone NumberValid? (2/6)
202-588-6500✓ Valid
67-500-647✗ Invalid
2025886500✗ Invalid
123-456-7890✓ Valid
12-34-5678✗ Invalid
abc-def-ghij✗ Invalid

Click through the different patterns. Notice how the "With +" version is too loose. It matches 67-500-647 because \d+ allows any number of digits, not just three.

Boundaries

Sometimes you want to match a pattern only in certain positions. Maybe you want to find the word "cat" but not match "catalog" or "scattered".

Boundary markers let you do exactly that.

Boundaries

See how ^ $ and \\b control where patterns match

Pattern: /cat/g
Matches "cat" anywhere
Result (4 matches)
cat catalog scattered scat

Here are the key markers:

MarkerMeaning
^Start of string
$End of string
\bWord boundary

The word boundary \b is especially useful. It matches the position between a word character and a non-word character. It doesn't match any actual character. It matches a position.

So /\bcat\b/ means: match "cat" only when it appears as a complete word.

Groups

Parentheses group parts of a pattern together. This is useful when you want to apply a quantifier to multiple characters at once.

/(ho)+/ // matches "ho", "hoho", "hohoho", etc.

The pipe | lets you match alternatives:

/cat|dog/ // matches "cat" or "dog"

You can combine these:

/watch(es|ed|er)?/ // matches watch, watches, watched, watcher

Groups with quantifiers

See which strings match the pattern

//
hamatch
hahamatch
hahahamatch
hno match
ahamatch
hahmatch

Find and replace

Regex really shines when you're transforming text. JavaScript's replace() method can take a regex pattern as its first argument.

Basic replacement

See how regex replace works

//
Before
haha
After
heha
💡 Try adding g flag for global replace, or use $1, $2 to reference capture groups

Without the g flag, only the first match gets replaced. Add g to replace all matches.

Here's where it gets really useful. You can reference captured groups in the replacement string using $1, $2, and so on.

Swapping words

See how regex replace works

//
Before
hello world
After
world hello
💡 Try adding g flag for global replace, or use $1, $2 to reference capture groups

The pattern (\w+) (\w+) captures two words. In the replacement, $1 is the first captured word and $2 is the second. So "hello world" becomes "world hello".

Quick reference

Here's everything we covered:

Character classes

  • \d matches any digit
  • \w matches any word character
  • \s matches any whitespace
  • . matches any character except newline
  • [abc] matches a, b, or c
  • [^abc] matches anything except a, b, or c
  • [a-z] matches any character from a to z

Quantifiers

  • + means one or more
  • * means zero or more
  • ? means zero or one
  • {n} means exactly n times
  • {n,m} means n to m times

Anchors

  • ^ matches start of string
  • $ matches end of string
  • \b matches word boundary

Groups

  • (abc) creates a capture group
  • a|b matches a or b

Flags

  • i for case insensitive
  • g for global (match all)

Playground

The best way to learn regex is to experiment. Here's a sandbox to try things out.

Experiment

Edit the pattern and test string to see matches

//
Result (2 matches)
The quick brown fox jumps over the lazy dog
Matches found:
[0]"over"at index 26
[1]"lazy"at index 35

Try these challenges:

  1. Match all words that start with a vowel
  2. Match words that are exactly 5 letters long
  3. Match "the" only when it starts with a capital T

Regex takes practice. The syntax is dense, but once it clicks, you'll find yourself reaching for it all the time.