This lesson puts every regex piece together into practical, reusable patterns: the ones you will actually need for forms and text cleanup. Each is broken down piece by piece so you can adapt it instead of copying blindly.
^ and $ for validationconst zip = /^\d{5}(?:-\d{4})?$/;
// 5 digits optional group: dash + 4 digits
zip.test("21201") // true
zip.test("21201-4410") // true
zip.test("2120") // false
const phone = /^\(?\d{3}\)?[-. ]?\d{3}[-. ]?\d{4}$/;
// optional ( ) around area code, optional separators
phone.test("410-555-0199") // true
phone.test("(410) 555-0199") // true
phone.test("4105550199") // true
phone.test("555-0199") // false
const email = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
// something @ something . at least 2 more, no spaces or extra @
email.test("ava@school.org") // true
email.test("ava@school") // false
email.test("ava @school.org") // false
<input type="email"> does a solid check for you.
const hex = /^#(?:[0-9a-f]{3}){1,2}$/i;
// # then 3 hex digits, once or twice (so 3 or 6 total)
hex.test("#fff") // true
hex.test("#1A2b3C") // true
hex.test("#abcd") // false
const isoDate = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
// month 01-12 day 01-31
isoDate.test("2026-10-02") // true
isoDate.test("2026-13-02") // false (month 13)
isoDate.test("2026-02-31") // true (!)
Date (see JS Dates).
const time24 = /^([01]\d|2[0-3]):[0-5]\d$/;
time24.test("09:45") // true
time24.test("23:59") // true
time24.test("24:00") // false
const username = /^[a-z][a-z0-9_]{2,15}$/i;
// starts with a letter, then 2-15 letters/digits/underscores (3-16 total)
// Collapse extra spaces
"too many spaces".replace(/\s+/g, " ") // "too many spaces"
// URL slug from a title
"Hello, World! JS 101".toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-|-$/g, "") // "hello-world-js-101"
// Strip HTML tags (for simple, trusted text only)
"<p>Hi <b>there</b></p>".replace(/<[^>]+>/g, "") // "Hi there"
// Capitalize each word
"the quick fox".replace(/\b\w/g, c => c.toUpperCase()) // "The Quick Fox"
W3Schools' "Patterns" page is a one-page reference for every piece of regex syntax. Here it is, with the lesson that teaches each part. Every pattern above is built from these pieces.
| d | Adds [start, end] indices to results (ES2022) |
| g | Find all matches |
| i | Ignore case |
| m | ^ and $ match at every line |
| s | . also matches line breaks (ES2018) |
| u | Unicode mode (ES2015) |
| v | An upgraded u with set math (ES2024) |
| y | Sticky: match only at lastIndex (ES2015) |
| [abc] | Any ONE of a, b, or c |
| [^abc] | Any one character except a, b, or c |
| [a-z] | Any one character from a to z |
| [^a-z] | Any one character NOT from a to z |
| [0-9] | Any one digit (same as \d) |
| [^0-9] | Any one non-digit (same as \D) |
| a|b | a or b |
| . | Any character except line breaks |
| \w \W | Word character (A-Z, a-z, 0-9, _) / not one |
| \d \D | Digit / not a digit |
| \s \S | Whitespace / not whitespace |
| \n \r \t | Newline, carriage return, tab |
| \v \f | Vertical tab, form feed |
| \0 | The NUL character (character code 0) |
| [\b] | Backspace (only inside [ ]; outside, \b is a word boundary) |
| \p{...} \P{...} | Has / lacks a Unicode property (needs u or v) |
| \xhh \uhhhh | A character by its hex code |
| \ddd | A character by its octal code (not recommended) |
| ^ $ | Start / end of the text (of each line with m) |
| \b \B | A word boundary / not a word boundary |
| (?=...) (?!...) | Followed by / not followed by |
| (?<=...) (?<!...) | Preceded by / not preceded by (ES2018) |
| x+ x* x? | 1 or more / 0 or more / 0 or 1 |
| x{n} x{n,} x{n,m} | Exactly n / n or more / n to m |
| +? *? ?? {n,m}? | Lazy: as few as possible |
A few of these have not appeared in an example yet:
const text = "Café 42!";
console.log(text.match(/\p{L}/gu).join("")); // letters, in any language
console.log(text.match(/\P{L}/gu)); // everything that is NOT a letter
console.log(text.match(/\S/g).length); // characters that are not whitespace
console.log(/[\b]/.test("a\bb"), /\t/.test("a\tb"), /\0/.test("a\0b"));
d as "Performs substring matches." The d flag does not change what matches at all; it adds the match positions (indices).v flag "new 2025." v arrived in ES2024, and W3Schools' own Methods page dates the same flag 2023.[abc] "matches all characters between the brackets" wording, covered in RegExp Classes: a class matches any one of them.| Zip | /^\d{5}(?:-\d{4})?$/ |
| Phone | /^\(?\d{3}\)?[-. ]?\d{3}[-. ]?\d{4}$/ |
| /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/ | |
| Hex color | /^#(?:[0-9a-f]{3}){1,2}$/i |
| Date | /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/ |
| 24h time | /^([01]\d|2[0-3]):[0-5]\d$/ |
Which pattern accepts "#ABC" and "#aabbcc" but rejects "#abcd"?
/^#[0-9a-f]+$/i/^#(?:[0-9a-f]{3}){1,2}$/i/^#[0-9a-f]{3,6}$/i/#[0-9a-f]{3}/iWhat does this log?
const time24 = /^([01]\d|2[0-3]):[0-5]\d$/;
console.log(time24.test("7:30"), time24.test("07:30"));
false true. The hour part needs exactly two digits, so "7:30" fails the shape check.What does [\b] match, and how is that different from \b?
[\b] matches the backspace character. Outside brackets, \b is an assertion: a word boundary, which matches a position and no character at all.Date check so "2026-02-31" is rejected