Regular Expressions for Beginners: A Practical Guide
What is a Regular Expression?
A regular expression (regex) is a compact pattern that describes a set of strings. It’s the tool you reach for when you need to validate an email, extract all URLs from a page, or replace every instance of “colour” with “color” in a thousand files.
Regex looks cryptic at first glance—^\d{4}-\d{2}-\d{2}$—but it’s built from a small set of rules. Once you learn the building blocks, you can read and write patterns with confidence.
You can experiment with patterns live in the Regex Tester on CodeKit.
The Building Blocks
Literal characters
Most characters match themselves. The pattern cat matches the string “cat” anywhere it appears.
Character classes
Square brackets define a set of allowed characters:
| Pattern | Matches |
|---|---|
[aeiou] | Any single vowel |
[a-z] | Any lowercase letter |
[A-Za-z0-9] | Any alphanumeric character |
[^0-9] | Any character that is not a digit |
Shorthand classes
Regex provides shortcuts for common character sets:
| Shorthand | Meaning | Equivalent |
|---|---|---|
\d | Digit | [0-9] |
\D | Non-digit | [^0-9] |
\w | Word character | [A-Za-z0-9_] |
\W | Non-word character | [^A-Za-z0-9_] |
\s | Whitespace | [ \t\n\r\f] |
\S | Non-whitespace | [^ \t\n\r\f] |
. | Any character (except newline) | — |
Anchors
Anchors don’t match characters—they match positions:
| Anchor | Meaning |
|---|---|
^ | Start of the string (or line, in multiline mode) |
$ | End of the string (or line) |
\b | Word boundary |
\bcat\b
This matches “cat” as a whole word but not the “cat” inside “category” or “concatenate”.
Quantifiers
Quantifiers specify how many times the preceding element should repeat:
| Quantifier | Meaning |
|---|---|
* | Zero or more |
+ | One or more |
? | Zero or one (optional) |
{n} | Exactly n times |
{n,} | At least n times |
{n,m} | Between n and m times |
\d{4} // exactly 4 digits (e.g., a year)
\d{1,3} // 1 to 3 digits
colou?r // "color" or "colour"
https? // "http" or "https"
By default, quantifiers are greedy—they match as much as possible. Add a ? to make them lazy:
<.*> // greedy: matches from first < to last >
<.*?> // lazy: matches from first < to first >
Grouping and Capturing
Parentheses create groups. By default, they also capture the matched text so you can reference it later.
(\d{4})-(\d{2})-(\d{2})
This matches a date like 2025-06-15 and captures three groups: year, month, and day.
const match = '2025-06-15'.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(match[1]); // "2025" (year)
console.log(match[2]); // "06" (month)
console.log(match[3]); // "15" (day)
Non-capturing groups
If you only need grouping (not the captured value), use (?:...) to avoid the overhead of storing the match:
(?:https?|ftp):// // groups the protocol but doesn't capture it
Backreferences
You can refer to a previously captured group with \1, \2, etc. This is useful for matching repeated patterns:
(\w+)\s\1 // matches a word followed by the same word
// "hello hello" âś“, "hello world" âś—
Lookahead and Lookbehind
Assertions let you check what comes before or after a position without consuming characters. This is powerful for validation.
| Assertion | Meaning |
|---|---|
(?=...) | Positive lookahead |
(?!...) | Negative lookahead |
(?<=...) | Positive lookbehind |
(?<!...) | Negative lookbehind |
\d+(?= dollars) // a number followed by " dollars"
\d+(?! dollars) // a number NOT followed by " dollars"
(?<=\$)\d+ // a number preceded by "$"
A classic use case is password validation—requiring at least one uppercase letter, one digit, and one special character:
^(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$
This reads: “from start to end, somewhere ahead there’s an uppercase letter, somewhere ahead there’s a digit, somewhere ahead there’s a special character, and the whole thing is at least 8 characters.”
Common Patterns Every Developer Should Know
Email validation
^[^\s@]+@[^\s@]+\.[^\s@]+$
This is a pragmatic email pattern—simple and good enough for most forms. For strict RFC 5322 compliance, the regex is famously enormous (over 6,000 characters). Don’t try to be perfect; validate with a confirmation email instead.
URL extraction
https?:\/\/[\w\-]+(\.[\w\-]+)+[\w\-.,@?^=%&:/~+#]*[^\s.,;]
IPv4 address
^(?:25[0-5]|2[0-4]\d|1?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|1?\d{1,2})){3}$
Phone number (US format)
^\+?1?[-.\s]?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})$
Matches +1 (555) 123-4567, 555-123-4567, 5551234567, and similar variants.
Date (ISO 8601)
^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?$
Practical Examples
Extracting all hashtags from text
const text = "Loving the #sunset and #beach vibes today #summer2025";
const hashtags = text.match(/#\w+/g);
console.log(hashtags); // ["#sunset", "#beach", "#summer2025"]
Replacing sensitive data
// Mask all but the last 4 digits of a credit card
const card = "4111 1111 1111 1111";
const masked = card.replace(/\d(?=\d{4})/g, '*');
console.log(masked); // "************1111"
Splitting on multiple delimiters
// Split on commas, semicolons, or pipes (with optional whitespace)
const parts = "a, b; c | d".split(/[,;|]\s*/);
console.log(parts); // ["a", "b", "c", "d"]
Performance Tips
Regex is powerful, but poorly written patterns can cause catastrophic backtracking—where the engine takes exponential time on certain inputs. This is the basis of ReDoS (Regular Expression Denial of Service) attacks.
1. Be specific
// Bad: matches anything, then anything, then anything
.*.*.*
// Good: match exactly what you need
\d{4}-\d{2}-\d{2}
2. Avoid nested quantifiers
// Dangerous: (a+)+ can backtrack exponentially on "aaaaaaaaaaaaaaaaaaaab"
(a+)+
// Safer: use a possessive quantifier or atomic group (if supported)
(a++)+ // possessive (no backtracking)
3. Anchor your patterns
If you’re validating an entire string, always use ^ and $. Without anchors, \d{4} will match the first four digits of “12345678”.
4. Compile once, use many times
In languages that support compiled regex, reuse the compiled pattern instead of recompiling on every call:
// Compile once
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Use many times
function isValidEmail(email) {
return emailRegex.test(email);
}
Conclusion
Regex is a skill that pays dividends forever. Start with the basics—literals, character classes, quantifiers—and add grouping, assertions, and backreferences as you need them. Keep a cheat sheet handy, and when a pattern gets too complex, break it into smaller pieces or reach for a parsing library.
The most important habit: always test your patterns against real input, including edge cases. A regex that works on happy-path data can silently fail on malformed input.
Ready to practice? Open the Regex Tester on CodeKit, paste in a pattern, and see matches highlighted in real time—no installation required.