Regular expressions — regex for short — are one of those things developers avoid for years, then can't live without once they learn them. They look intimidating at first glance (/^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$/ — what?), but they're built from just a handful of simple concepts that stack together logically.
This guide walks through every building block from scratch. And because reading about regex without being able to test it is frustrating, you can try every example in the DevToolShack Regex Tester — live, in your browser, no sign-up needed.
What Is a Regular Expression?
A regular expression is a pattern that describes a set of strings. You use that pattern to search, match, extract, or replace text. Nearly every programming language supports regex: JavaScript, Python, PHP, Ruby, Java, Go — they all have built-in regex engines.
In most languages, a regex pattern is written between forward slashes: /pattern/flags. The flags modify how the match works — more on those below.
The Simplest Pattern: Literal Text
The simplest regex is just the text you're looking for:
hello
This matches the exact string hello anywhere in the input. Case-sensitive by default — it won't match Hello or HELLO.
hello as your pattern, and paste in some text. Watch it highlight every match in real time as you type.Flags: Modifying How Matches Work
Flags go after the closing slash and change the matching behavior:
| Flag | Name | What it does |
|---|---|---|
i | Case-insensitive | Matches Hello, HELLO, hello equally |
g | Global | Finds all matches, not just the first one |
m | Multiline | Makes ^ and $ match start/end of each line |
s | Dotall | Makes . match newline characters too |
So /hello/gi finds every occurrence of "hello" regardless of capitalisation.
The Dot: Match Any Character
A dot . matches any single character except a newline:
h.t
This matches hat, hit, hot, h3t, h t — any character in the middle position. To match a literal dot, escape it: \.
Character Classes: Match One of These
Square brackets define a character class — match any one character from the set:
[aeiou]
Matches any single vowel. You can also use ranges:
[a-z] # any lowercase letter
[A-Z] # any uppercase letter
[0-9] # any digit
[a-zA-Z0-9] # any letter or digit
A caret ^ at the start of a character class negates it:
[^aeiou] # any character that is NOT a vowel
Shorthand Character Classes
These are so common they have shortcuts:
| Shorthand | Equivalent | Matches |
|---|---|---|
\d | [0-9] | Any digit |
\D | [^0-9] | Any non-digit |
\w | [a-zA-Z0-9_] | Any word character |
\W | [^a-zA-Z0-9_] | Any non-word character |
\s | [ \t\r\n] | Any whitespace |
\S | [^ \t\r\n] | Any non-whitespace |
Quantifiers: How Many Times?
Quantifiers go after a character or group and say how many times it should match:
| Quantifier | Meaning |
|---|---|
* | Zero or more |
+ | One or more |
? | Zero or one (optional) |
{3} | Exactly 3 |
{2,5} | Between 2 and 5 |
{2,} | 2 or more |
So \d+ matches one or more digits, and \d{3}-\d{4} matches a US local phone number format like 555-1234.
Anchors: Position in the String
Anchors don't match characters — they match positions:
^— matches the start of the string (or line in multiline mode)$— matches the end of the string (or line)\b— matches a word boundary
^\d{5}$
This matches a string that is exactly 5 digits — like a US ZIP code. Without the anchors, \d{5} would also match the digits inside abc12345xyz.
Groups and Alternation
Parentheses create a group — they let you apply quantifiers to multiple characters, or capture part of the match:
(cat|dog)s?
This matches cat, cats, dog, or dogs. The pipe | means "or", and the s? makes the "s" optional.
Real-World Patterns to Steal
Here are some battle-tested patterns you can use immediately — paste them into the Regex Tester to see them in action:
# Email address (basic)
^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$
# US phone number (flexible)
\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}
# URL
https?://[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?
# IPv4 address
\b(\d{1,3}\.){3}\d{1,3}\b
# Date (YYYY-MM-DD)
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])
# Hex color code
#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})
Greedy vs Lazy Matching
By default, quantifiers are greedy — they match as much as possible. Add a ? after the quantifier to make it lazy — match as little as possible:
# Input: <b>bold</b> and <i>italic</i>
<.+> # Greedy: matches the whole thing from first < to last >
<.+?> # Lazy: matches <b>, then </b>, then <i>, then </i> separately
This trips up a lot of beginners — if your pattern is matching too much, try adding ? after your quantifier.
Using Regex in JavaScript
const email = "ada@example.com";
const pattern = /^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$/;
// Test if it matches
pattern.test(email); // true
// Extract all matches from a string
const text = "Call 555-1234 or 555-5678";
const phones = text.match(/\d{3}-\d{4}/g); // ["555-1234", "555-5678"]
// Replace matches
const cleaned = text.replace(/\d{3}-\d{4}/g, "[REDACTED]");
// "Call [REDACTED] or [REDACTED]"