Regex patterns every developer should know
Most regular expressions people actually need in day-to-day work come from a small, reusable set. Memorising the syntax matters less than knowing which pattern to reach for.
Whitespace and trimming
^\s+|\s+$ matches leading and trailing whitespace, which is what String.prototype.trim() does internally. \s+ collapses runs of spaces, tabs and newlines into one match, useful when normalising pasted text before storage.
Emails: good enough, not perfect
A fully RFC 5322-compliant email regex is enormous and still wrong at the edges. For form validation, a pragmatic pattern works better paired with a confirmation email:
const emailLike = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
This rejects obviously malformed input without pretending to be a full validator. Never use regex alone to decide whether an email address can actually receive mail.
URLs and paths
^https?:\/\/[\w.-]+(?:\/[\w./?%&=-]*)?$ catches the common case of an http(s) URL with an optional path and query string. For anything more precise, parse with the URL API instead — a URL parser tool exists exactly because regex is the wrong tool for full RFC 3986 compliance.
Capturing groups vs non-capturing groups
Use (?:...) when you need to group alternatives but do not need the matched text back — it is faster and keeps your capture group indices predictable when the pattern grows.
const version = /^v(?:ersion)?[\s-]?(\d+)\.(\d+)\.(\d+)$/;
Here only the three numeric groups are captured; the optional "ersion" text is grouped but ignored.
Lookaheads for validation without consuming
Password strength rules are the classic use case:
(?=.*[a-z])— at least one lowercase letter(?=.*[A-Z])— at least one uppercase letter(?=.*\d)— at least one digit(?=.*[^\w\s])— at least one symbol
Chain lookaheads at the start of the pattern, then follow with the length requirement: ^(?=.[a-z])(?=.[A-Z])(?=.*\d).{8,}$.
Greedy vs lazy quantifiers
. is greedy and will match as much as possible before backtracking. .? is lazy and matches as little as possible. Extracting content between HTML-like tags is the classic trap:
"<b>one</b><b>two</b>".match(/<b>(.*)<\/b>/)[1]; // "one</b><b>two"
"<b>one</b><b>two</b>".match(/<b>(.*?)<\/b>/)[1]; // "one"
Escaping special characters
If you are building a pattern from user input, escape regex metacharacters first, or the input can change what the pattern matches entirely:
function escapeRegex(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
Catastrophic backtracking
Nested quantifiers like (a+)+ can take exponential time on pathological input — a classic ReDoS. Avoid nesting repetition, prefer atomic patterns, and test suspicious patterns against a long string of near-matches before shipping them anywhere user input reaches the pattern.
Test before you commit
Write the pattern, then run it against a batch of both matching and non-matching examples in a regex tester with live highlighting. Seeing which group matched what removes almost all of the guesswork, especially with lookaheads and nested groups.