SKU // 92

JavaScript RegExp Patterns

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.

// How to Build a Pattern

  1. Write down 3 examples that should match and 3 that should not
  2. Describe the shape in words: "3 digits, a dash, 4 digits"
  3. Translate each word into a regex piece
  4. Anchor with ^ and $ for validation
  5. Test every example, including the ones that should fail

// US Zip Code (with optional +4)

const 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
true true false

// US Phone Number (flexible)

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
true true true false

// Email (practical, not perfect)

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
Reality Check
The full rules for valid email addresses are enormous, and no short regex gets them all. Use a simple pattern to catch typos, then confirm the address is real by sending a message to it. In HTML forms, <input type="email"> does a solid check for you.

// Hex Color

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

// Date: YYYY-MM-DD

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   (!)
true false true
Note
A regex checks shape, not meaning. It cannot know February has no 31st. Check the shape with a regex, then check the value with a Date (see JS Dates).

// 24-Hour Time

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
true true false

// Username

const username = /^[a-z][a-z0-9_]{2,15}$/i;
//               starts with a letter, then 2-15 letters/digits/underscores (3-16 total)

// Text Cleanup Patterns

// 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"
too many spaces hello-world-js-101 Hi there The Quick Fox

// The Full Syntax Reference

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.

Flags (RegExp Flags)

dAdds [start, end] indices to results (ES2022)
gFind all matches
iIgnore case
m^ and $ match at every line
s. also matches line breaks (ES2018)
uUnicode mode (ES2015)
vAn upgraded u with set math (ES2024)
ySticky: match only at lastIndex (ES2015)

Character classes (RegExp Classes)

[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)

Metacharacters (RegExp Metacharacters)

a|ba or b
.Any character except line breaks
\w \WWord character (A-Z, a-z, 0-9, _) / not one
\d \DDigit / not a digit
\s \SWhitespace / not whitespace
\n \r \tNewline, carriage return, tab
\v \fVertical tab, form feed
\0The 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 \uhhhhA character by its hex code
\dddA character by its octal code (not recommended)

Assertions (RegExp Assertions)

^ $Start / end of the text (of each line with m)
\b \BA word boundary / not a word boundary
(?=...) (?!...)Followed by / not followed by
(?<=...) (?<!...)Preceded by / not preceded by (ES2018)

Quantifiers (RegExp Quantifiers)

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"));
Café [" ", "4", "2", "!"] 7 true true true
W3Schools errors on its Patterns page
  • It describes d as "Performs substring matches." The d flag does not change what matches at all; it adds the match positions (indices).
  • It dates the v flag "new 2025." v arrived in ES2024, and W3Schools' own Methods page dates the same flag 2023.
  • Its [abc] "matches all characters between the brackets" wording, covered in RegExp Classes: a class matches any one of them.

// Quick Reference

Zip/^\d{5}(?:-\d{4})?$/
Phone/^\(?\d{3}\)?[-. ]?\d{3}[-. ]?\d{4}$/
Email/^[^\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$/

// Quick Check

Which pattern accepts "#ABC" and "#aabbcc" but rejects "#abcd"?

  1. /^#[0-9a-f]+$/i
  2. /^#(?:[0-9a-f]{3}){1,2}$/i
  3. /^#[0-9a-f]{3,6}$/i
  4. /#[0-9a-f]{3}/i
Show answer
B. A accepts any length. C accepts 4 or 5 digits too. D has no anchors, so "#abcd" contains a match. Only B forces exactly 3 or 6.

What does this log?

const time24 = /^([01]\d|2[0-3]):[0-5]\d$/;
console.log(time24.test("7:30"), time24.test("07:30"));
Show answer
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?

Show answer
Inside square brackets, [\b] matches the backspace character. Outside brackets, \b is an assertion: a word boundary, which matches a position and no character at all.

// Try It

  1. Click Open Playground. Test the phone pattern with 6 inputs you write yourself: 3 good, 3 bad
  2. Write the username pattern and change it so it must be all lowercase
  3. Write a pattern for a license plate in the format 1 digit, 3 letters, 3 digits (like "1ABC234")
  4. Combine the ISO date regex with a Date check so "2026-02-31" is rejected
// Video Walkthroughs
Last updated: Oct 10, 2026, 1:47 AM ET