Regular expressions are one of those tools that look intimidating at first but become indispensable once you understand them. This guide covers the patterns you'll actually use in JavaScript projects — from form validation to parsing log files.
The Basics You Need to Know First
A regex in JavaScript looks like /pattern/flags. You can use it two ways:
// Method 1: Regex literal (preferred for static patterns)
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Method 2: RegExp constructor (use when pattern is dynamic)
const term = "error";
const logRegex = new RegExp(`\\b${term}\\b`, "gi");
The most important flags:
-
g— global, find all matches (not just the first) -
i— case-insensitive -
m— multiline,^and$match line start/end -
s— dotAll,.matches newlines too
The 10 Patterns That Cover 80% of Use Cases
1. Email Validation
const isEmail = (str) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
isEmail("user@example.com"); // true
isEmail("not-an-email"); // false
Note: This catches obvious non-emails without being overly strict. RFC 5322 compliant regex is a 6-kilobyte nightmare — don't bother.
2. Phone Numbers (Flexible)
// Matches: (555) 123-4567, 555-123-4567, 5551234567, +1 555 123 4567
const phoneRegex = /^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/;
3. Extract All URLs from Text
const extractUrls = (text) => {
const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g;
return text.match(urlRegex) || [];
};
extractUrls("Visit https://example.com and https://dev.to for resources");
// ["https://example.com", "https://dev.to"]
4. Strong Password Validation
// At least 8 chars, one uppercase, one lowercase, one digit, one special char
const isStrongPassword = (pwd) =>
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/.test(pwd);
This uses lookaheads ((?=...)), which check for a condition without consuming characters. Each (?=.*[A-Z]) asserts "somewhere ahead there's an uppercase letter."
5. Normalize Whitespace
// Remove multiple spaces, collapse to single space
const normalizeSpaces = (str) => str.replace(/\s+/g, " ").trim();
normalizeSpaces("Hello World "); // "Hello World"
6. Slugify a String (for URLs)
const slugify = (str) =>
str
.toLowerCase()
.replace(/[^\w\s-]/g, "") // remove special chars
.replace(/[\s_-]+/g, "-") // spaces/underscores to hyphens
.replace(/^-+|-+$/g, ""); // trim leading/trailing hyphens
slugify("Hello, World! This is a Test."); // "hello-world-this-is-a-test"
7. Extract Numbers from a String
const extractNumbers = (str) => str.match(/\d+(\.\d+)?/g)?.map(Number) || [];
extractNumbers("The price is $29.99 and tax is 3.5%"); // [29.99, 3.5]
8. Validate Hex Colors
const isHexColor = (str) => /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(str);
isHexColor("#fff"); // true
isHexColor("#FF5733"); // true
isHexColor("red"); // false
9. Find and Replace with Captured Groups
This is where regex gets really powerful. Parentheses () capture matched groups you can reference in replacements with $1, $2:
// Reformat date from MM/DD/YYYY to YYYY-MM-DD
const reformatDate = (date) => date.replace(/(\d{2})\/(\d{2})\/(\d{4})/, "$3-$1-$2");
reformatDate("03/21/2026"); // "2026-03-21"
// Wrap all numbers in <strong> tags
const boldNumbers = (str) => str.replace(/(\d+)/g, "<strong>$1</strong>");
boldNumbers("You have 3 new messages and 12 pending tasks");
// "You have <strong>3</strong> new messages and <strong>12</strong> pending tasks"
10. Parse Query Strings
const parseQueryString = (qs) => {
const params = {};
const regex = /[?&]([^=#]+)=([^&#]*)/g;
let match;
while ((match = regex.exec(qs)) !== null) {
params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
}
return params;
};
parseQueryString("?name=John&age=30&city=New%20York");
// { name: "John", age: "30", city: "New York" }
Common Mistakes That Will Bite You
Forgetting the g Flag
// BUG: Only replaces the first occurrence
"aaa".replace(/a/, "b"); // "baa"
// Fix: Use the g flag
"aaa".replace(/a/g, "b"); // "bbb"
Escaping Special Characters in User Input
If you're building a regex from user input, always escape it first:
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
// Safe search highlight
const highlightTerm = (text, term) => {
const escaped = escapeRegex(term);
return text.replace(new RegExp(`(${escaped})`, "gi"), "<mark>$1</mark>");
};
Without escapeRegex, a user searching for (test) would throw a regex syntax error.
The Catastrophic Backtracking Trap
This regex looks fine but will hang your browser on certain inputs:
// DANGEROUS — exponential backtracking
/^(a+)+$/.test("aaaaaaaaaaaaaaaaaaaaaaaaaaab");
The fix: avoid nested quantifiers on overlapping patterns. Test your regex with adversarial inputs before shipping.
Debugging Complex Patterns
When a regex isn't working, break it into smaller pieces and test each part in isolation. I use the free developer tools at Profiterole for quick testing — being able to visually see which groups match what saves a lot of trial and error. If you're also working with TypeScript and extracting structured data, their JSON-to-TypeScript converter pairs well once you've got your regex outputting the right shape.
Quick Reference Cheat Sheet
| Symbol | Meaning |
|---|---|
. |
Any character except newline |
\d |
Digit [0-9] |
\w |
Word char [a-zA-Z0-9_] |
\s |
Whitespace |
^ |
Start of string/line |
$ |
End of string/line |
* |
0 or more |
+ |
1 or more |
? |
0 or 1 |
{n,m} |
Between n and m times |
(...) |
Capture group |
(?:...) |
Non-capturing group |
(?=...) |
Positive lookahead |
(?!...) |
Negative lookahead |
[abc] |
Character class |
[^abc] |
Negated character class |
Putting It All Together
Here's a real-world function that sanitizes and validates a user comment:
function processComment(raw) {
// Remove script tags
const noScript = raw.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "");
// Normalize whitespace
const normalized = noScript.replace(/\s+/g, " ").trim();
// Check for spam patterns
const spamPattern = /\b(buy now|click here|free money|earn \$\d+)\b/i;
if (normalized.length < 10) return { valid: false, reason: "Too short" };
if (spamPattern.test(normalized)) return { valid: false, reason: "Spam detected" };
return { valid: true, content: normalized };
}
Regular expressions reward the time you put in. The 10 patterns above cover the vast majority of what you'll encounter day-to-day — bookmark this page and the next time you're tempted to reach for a string library, try a quick regex first.
What regex pattern do you find yourself rewriting from scratch most often? Share it in the comments.
Top comments (0)