Regular Expressions: A Practical Reference for Developers
Regular expressions appear in almost every developer's toolkit, yet they're often written once, copy-pasted forever, and never fully understood. This reference covers the syntax you actually need and the patterns you'll reach for most often.
Test regex live: Our Regex Tester highlights matches as you type, shows capture groups, and supports all JavaScript regex flags.
Open Regex Tester โTable of Contents
Character Classes
| Pattern | Matches |
|---|---|
. | Any character except newline |
\d | Digit [0-9] |
\w | Word character [a-zA-Z0-9_] |
\s | Whitespace (space, tab, newline) |
\D \W \S | Negation of above |
[abc] | Any of a, b, or c |
[^abc] | Any character except a, b, c |
[a-z] | Any lowercase letter |
Quantifiers
| Pattern | Meaning |
|---|---|
* | Zero or more (greedy) |
+ | One or more (greedy) |
? | Zero or one |
{n} | Exactly n |
{n,m} | Between n and m |
*? +? ?? | Lazy (match as little as possible) |
Anchors
^ Start of string (or line with m flag)
$ End of string (or line with m flag)
Word boundary
\B Not a word boundary
Common Patterns
Email (pragmatic)
/^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$/
URL
/^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,6}([-a-zA-Z0-9@:%_+.~#?&/=]*)$/
ISO 8601 date
/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/
UUID v4
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
Hex color
/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/
Strong password
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
US phone (flexible)
/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4}$/
Named Capture Groups
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const { year, month, day } = "2026-02-24".match(re).groups;
Lookahead and Lookbehind Assertions
Lookarounds match a position in the string based on what comes before or after it, without including those characters in the match. They are zero-width โ they don't consume characters.
// Positive lookahead (?=...) โ match X followed by Y, return only X
/\d+(?= dollars)/ // matches "100" in "100 dollars" (not "dollars")
// Negative lookahead (?!...) โ match X NOT followed by Y
/\d+(?! dollars)/ // matches numbers NOT followed by " dollars"
// Positive lookbehind (?<=...) โ match X preceded by Y
/(?<=\$)\d+/ // matches "100" in "$100" (not the $)
// Negative lookbehind (?
Common practical uses:
- Extract value without unit:
/\d+(?=px)/โ matches the number before "px" - Password validation:
/(?=.*[A-Z])(?=.*\d).{8,}/โ must contain uppercase AND digit, 8+ chars - Find word not in context:
/(?<!un)happy/โ matches "happy" but not "unhappy" - Strip currency symbol:
/(?<=[$โฌยฃ])\d+[\d.,]*/โ matches amount after any currency symbol
Browser support: Positive and negative lookaheads are supported in all modern JavaScript engines. Lookbehinds ((?<=...) and (?<!...)) were added in ES2018 and are supported in all current browsers (Chrome 62+, Firefox 78+, Safari 16.4+). Avoid lookbehinds if you need to support very old environments.
Flags
| Flag | Effect |
|---|---|
g | Global โ find all matches |
i | Case-insensitive |
m | Multiline โ ^ and $ match line boundaries |
s | DotAll โ . matches newlines |
u | Unicode |
Debugging Regular Expressions
When a regex doesn't match what you expect โ or matches too much โ the fastest debugging approach is to build up incrementally. Start with the simplest possible pattern that matches part of your target, then add one element at a time. If /\d{3}-\d{3}-\d{4}/ isn't matching phone numbers in your text, strip it back to /\d{3}/ and verify the first group matches. If it doesn't, the issue is in your input data (maybe the digits are Unicode full-width characters, or there are invisible characters between them).
Regex testers with visual match highlighting โ like the Regex Tester โ make this process much faster than running code in a loop. Paste your real data, not simplified test strings, because production data always contains edge cases you didn't anticipate: extra whitespace, mixed encodings, or control characters.
Performance Considerations
Most regex performance problems come from catastrophic backtracking, where the engine explores an exponential number of paths before determining that a match is impossible. The classic example is nested quantifiers like (a+)+ applied to a string of a's followed by a non-matching character. Each additional a doubles the number of ways the engine tries to partition the string between the inner and outer quantifier.
The fix is to avoid ambiguous alternatives. If two parts of your pattern can match the same character, the engine has to try both possibilities at every position. Make your patterns as specific as possible: use [^"]* instead of .* when matching the contents of a quoted string, because the negated character class can't match the closing quote and eliminates backtracking entirely.
In production code that processes untrusted input, set a timeout on regex execution. Most languages support this: .NET has Regex.MatchTimeout, Java's Pattern doesn't natively support timeouts but you can run matching in a thread with a deadline, and JavaScript's regex engine doesn't support timeouts, which is why server-side Node.js applications should use the re2 package (a linear-time regex engine) for user-provided patterns.
Regex in Different Contexts
Regex syntax varies more than you'd expect between tools and languages. JavaScript doesn't support lookbehind in older engines (pre-ES2018), Python's re module doesn't support atomic groups or possessive quantifiers (use the regex module instead), and grep's default mode uses basic regex where + and ? need escaping. Always test your pattern in the actual environment where it will run, not in a different tool.
Named capture groups are supported everywhere modern, but the syntax differs: JavaScript and Python use (?<name>...), while .NET and Java also support (?P<name>...) (Python) or just (?<name>...). When writing patterns that need to be portable, stick to numbered capture groups, or use a regex abstraction library.
