Search and validate text with patterns. Learn the syntax piece by piece, with practical examples for real form validation.

Bishal Biswas
Self Learner
What is a regular expression?
A regular expression, or regex, is a pattern for matching text. Instead of asking “does this string equal X”, you ask “does this string look like X”.
const pattern = /cat/;
console.log(pattern.test('the cat sat'));
console.log(pattern.test('a dog'));
Regex looks intimidating because it is dense. Take it one symbol at a time and it becomes readable.
Creating one
const literal = /hello/i;
const constructed = new RegExp('hello', 'i');
const word = 'hello';
const dynamic = new RegExp(word, 'i');
Use the /pattern/ form normally. Use new RegExp only when the pattern comes from a variable.
Flags
console.log(/cat/i.test('CAT'));
console.log('cat cat'.match(/cat/g));
iignores case.gfinds all matches, not just the first.mmakes^and$work per line.smakes.match newlines too.
Character classes
console.log(/[abc]/.test('banana'));
console.log(/[a-z]/.test('Hello'));
console.log(/[0-9]/.test('abc123'));
console.log(/[^0-9]/.test('123a'));
Square brackets mean “any one of these”. A ^ inside them means “none of these”.
Shorthand
These come up constantly:
console.log(/\d/.test('abc1'));
console.log(/\w/.test('hello_1'));
console.log(/\s/.test('a b'));
\dis any digit,\Dis any non digit.\wis a letter, digit or underscore,\Wis the opposite.\sis any whitespace,\Sis the opposite..is any character except a newline.
Quantifiers
How many times the thing before it may appear.
console.log(/\d+/.test('abc123'));
console.log(/colou?r/.test('color'));
console.log(/colou?r/.test('colour'));
console.log(/\d{3}/.test('12'));
console.log(/\d{3}/.test('123'));
console.log(/\d{2,4}/.test('12345'));
*means zero or more.+means one or more.?means zero or one.{n}means exactly n.{n,}means n or more.{n,m}means between n and m.
Anchors
console.log(/^hello/.test('hello world'));
console.log(/^hello/.test('say hello'));
console.log(/world$/.test('hello world'));
console.log(/^\d+$/.test('12345'));
console.log(/^\d+$/.test('123a'));
^ means start, $ means end. Without them your pattern can match anywhere inside the string, which is a very common validation bug.
Groups
Round brackets group parts together and capture what they matched.
console.log(/(ab)+/.test('ababab'));
console.log(/^(cat|dog)$/.test('dog'));
const match = '2026-09-05'.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(match[0]);
console.log(match[1], match[2], match[3]);
Named groups are much easier to read:
const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const { groups } = '2026-09-05'.match(pattern);
console.log(groups.year, groups.month, groups.day);
The methods
test for yes or no
console.log(/\d/.test('abc1'));
match to get the matches
console.log('a1b2c3'.match(/\d/));
console.log('a1b2c3'.match(/\d/g));
console.log('abc'.match(/\d/));
Without g you get details about the first match. With g you get a plain array of every match. No match gives null, so always check before using the result.
matchAll for details of every match
const text = 'Asha: 28, Ben: 35';
for (const m of text.matchAll(/(\w+): (\d+)/g)) {
console.log(m[1], 'is', m[2]);
}
replace and replaceAll
console.log('hello world'.replace(/o/g, '0'));
console.log('2026-09-05'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1'));
console.log('a1b2'.replace(/\d/g, (digit) => digit * 2));
$1, $2 refer to the captured groups. A function gives you full control.
split
console.log('a1b22c333d'.split(/\d+/));
console.log('a, b;c d'.split(/[,;\s]+/));
Practical patterns
const patterns = {
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
indianPhone: /^[6-9]\d{9}$/,
pincode: /^[1-9][0-9]{5}$/,
username: /^[a-zA-Z0-9_]{3,16}$/,
hexColour: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i,
onlyDigits: /^\d+$/
};
console.log(patterns.email.test('asha@example.com'));
console.log(patterns.indianPhone.test('9876543210'));
console.log(patterns.username.test('asha_01'));
On email validation: the pattern above is deliberately loose. The full official email grammar is enormous and matching it exactly is not worth the trouble. Check that it roughly looks like an address, then confirm by sending a verification mail.
Escaping
Characters like . and ? mean something special, so escape them with a backslash when you want them literally.
console.log(/3.14/.test('3814'));
console.log(/3\.14/.test('3814'));
console.log(/3\.14/.test('3.14'));
For a pattern built from user input, escape everything:
function escapeRegex(text) {
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const term = 'price (usd)';
const safe = new RegExp(escapeRegex(term), 'i');
console.log(safe.test('The price (usd) is 10'));
Greedy versus lazy
const html = '<b>bold</b> and <i>italic</i>';
console.log(html.match(/<.+>/)[0]);
console.log(html.match(/<.+?>/)[0]);
+ is greedy and grabs as much as it can. Adding ? makes it lazy and stops at the first opportunity.
Do not parse HTML with regex. For anything structured, use a real parser. Regex is for flat text.
Watch out for lastIndex
const pattern = /\d/g;
console.log(pattern.test('a1'));
console.log(pattern.test('a1'));
The second call gives false. A g flagged regex remembers where it stopped. Do not reuse one across test calls, or reset pattern.lastIndex = 0.
Quick recap
- Regex matches patterns rather than exact text.
\d,\wand\scover most character needs.*,+,?and{n,m}control how many times.- Anchor with
^and$for validation, or you will match partial strings. testgives a boolean,matchgives the matches,matchAllgives details of all.- Escape special characters, and escape any pattern built from user input.
- A
gflagged regex rememberslastIndexbetweentestcalls.
// test gives a simple yes or no
console.log(/cat/.test('the cat sat')); // true
console.log(/cat/i.test('The CAT sat')); // true, i ignores case
// Character shorthands and quantifiers
console.log(/^\d+$/.test('12345')); // true, digits only
console.log(/^\d+$/.test('123a')); // false, anchors matter
console.log(/colou?r/.test('color')); // true, ? means optional
// Groups capture the parts you want
const match = '2026-09-05'.match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(match[1], match[2], match[3]); // 2026 09 05
// Named groups are far easier to read
const datePattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const { groups } = '2026-09-05'.match(datePattern);
console.log(groups.day + '/' + groups.month + '/' + groups.year);
// Finding every match
console.log('a1b2c3'.match(/\d/g)); // ['1','2','3']
for (const m of 'Asha: 28, Ben: 35'.matchAll(/(\w+): (\d+)/g)) {
console.log(m[1], 'is', m[2]);
}
// Replacing, with $1 style references or a function
console.log('2026-09-05'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1'));
console.log('a1b2'.replace(/\d/g, d => d * 2)); // a2b4
// Practical validation patterns
const patterns = {
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
indianPhone: /^[6-9]\d{9}$/,
pincode: /^[1-9][0-9]{5}$/,
username: /^[a-zA-Z0-9_]{3,16}$/
};
console.log(patterns.email.test('asha@example.com')); // true
console.log(patterns.indianPhone.test('9876543210')); // true
console.log(patterns.pincode.test('411001')); // true
// Always escape a pattern built from user input
function escapeRegex(text) {
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const safe = new RegExp(escapeRegex('price (usd)'), 'i');
console.log(safe.test('The price (usd) is 10')); // true
// Greedy grabs everything, lazy stops early
const html = '<b>bold</b> and <i>italic</i>';
console.log(html.match(/<.+>/)[0]); // the whole thing, greedy
console.log(html.match(/<.+?>/)[0]); // just <b>, lazy
Was this explanation helpful?
Your feedback helps us improve the content for everyone.
Thank you for your feedback!
We appreciate you helping us improve.
What could we improve? (Optional)
Read aloud
Ready