Regular expressions are compact patterns for finding, extracting, and changing text. This guide covers the JavaScript syntax you will use most often.
What is a regular expression?
A regular expression, or regex, describes text you want to match.
This pattern matches the exact text hello:
/hello/Use it with a JavaScript string method:
const greeting = 'Well, hello'
console.log(greeting.replace(/hello/, 'hi'))
// Well, hiRegex is useful when a plain string is not flexible enough. It can match things like "a number at the end" or "every word between braces."
Creating a regex in JavaScript
JavaScript has two forms.
Regex literals
Write the pattern between forward slashes. Put optional flags after the closing slash:
const pattern = /hello/giLiterals are best when the pattern is known while writing the code.
The RegExp constructor
Use the constructor when part of the pattern comes from a variable:
const word = 'hello'
const pattern = new RegExp(word, 'gi')The first argument is a string. Backslashes inside it must be escaped:
const literal = /\d+/
const constructed = new RegExp('\\d+')Both patterns match one or more digits.
Literal characters and escaping
Most characters match themselves. /cat/ matches cat.
These characters have special meaning outside a character class:
. ^ $ * + ? ( ) [ ] { } | \Add a backslash to match one literally:
/\?/That pattern matches a question mark.
A forward slash is not part of regex syntax itself, but it closes a JavaScript regex literal. Escape it inside a literal:
const protocol = /https:\/\//You do not need to escape / in a constructor string:
const protocol = new RegExp('https://')Inside [], the rules change. Backslash and ] need care. A hyphen creates a
range unless it is escaped or placed first or last. A caret negates the class
only when it comes first.
Character matching
The dot .
A dot matches any single character except a line terminator:
/c.t/It matches cat, cut, and c3t. It does not match ct.
Use the s flag if the dot should also match line terminators:
console.log(/start.end/s.test('start\nend')) // trueCharacter classes []
A character class matches one character from a set:
/[aeiou]/Ranges keep classes short:
/[0-9]/
/[a-z]/
/[A-Fa-f0-9]/The last pattern matches one hexadecimal character.
Put ^ first to negate a class:
/[^0-9]/This matches one character that is not an ASCII digit. It does not mean "the whole string contains no digits." For that, anchor and repeat the class:
/^[^0-9]*$/Shorthand character classes
JavaScript provides common character classes as shorthands:
| Pattern | Matches |
|---|---|
\d | An ASCII digit, the same as [0-9] |
\D | Anything except an ASCII digit |
\w | An ASCII letter, digit, or underscore |
\W | Anything except an ASCII letter, digit, or underscore |
\s | A whitespace character |
\S | A non-whitespace character |
\t | A tab |
\r | A carriage return |
\n | A line feed |
Without Unicode-aware case folding, \w is the same as [A-Za-z0-9_]. It does
not match every letter in every language. Use a Unicode property escape when you
need that:
console.log(/^\p{Letter}+$/u.test('München')) // trueUnicode property escapes require the u or v flag.
Anchors and boundaries
Anchors match positions. They do not consume characters.
| Pattern | Position |
|---|---|
^ | Start of the string, or a line with the m flag |
$ | End of the string, or a line with the m flag |
\b | Between word and non-word characters, including an edge next to a word character |
\B | Any position that is not a word boundary |
Use both anchors to match an entire string:
console.log(/^cat$/.test('cat')) // true
console.log(/^cat$/.test('a cat')) // falseUse word boundaries to avoid partial words:
console.log(/\bcat\b/.test('a cat naps')) // true
console.log(/\bcat\b/.test('scatter')) // falseWord boundaries use the same mostly ASCII idea of a word as \w. They can be
surprising with non-Latin text.
Quantifiers
A quantifier repeats the token immediately before it.
| Quantifier | Meaning |
|---|---|
? | Zero or one |
* | Zero or more |
+ | One or more |
{3} | Exactly three |
{3,} | Three or more |
{3,5} | Between three and five |
The question mark makes the preceding token optional:
/colou?r/It matches color and colour.
The star allows zero matches:
console.log(/^\d*$/.test('')) // true
console.log(/^\d*$/.test('123')) // trueThe plus requires at least one:
console.log(/^\d+$/.test('')) // false
console.log(/^\d+$/.test('123')) // trueQuantifiers apply to one token. /hello*/ means hell followed by zero or more
o characters. Use a group to repeat the whole word: /(?:hello)*/.
Greedy and lazy matching
Quantifiers are greedy by default. They take as much text as possible:
'<b>one</b><i>two</i>'.match(/<.+>/)
// ['<b>one</b><i>two</i>']Add ? after a quantifier to make it lazy:
'<b>one</b><i>two</i>'.match(/<.+?>/g)
// ['<b>', '</b>', '<i>', '</i>']Lazy does not mean "safe HTML parser." Use the DOM when you need to understand HTML structure.
Alternation and groups
Alternation |
The pipe means "or":
/cat|dog/Alternation has low precedence. Group it when anchors or quantifiers should apply to every option:
console.log(/^(?:cat|dog)$/.test('dog')) // true
console.log(/^(?:cat|dog)$/.test('hotdog')) // falseCapturing groups ()
Parentheses group a pattern and capture its match:
const match = '2026-08-29'.match(/(\d{4})-(\d{2})-(\d{2})/)
console.log(match?.[0]) // 2026-08-29
console.log(match?.[1]) // 2026
console.log(match?.[2]) // 08
console.log(match?.[3]) // 29Captured values can be used in replacements:
const date = '2026-08-29'
console.log(date.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1'))
// 29/08/2026Non-capturing groups (?:)
Use a non-capturing group when you only need grouping:
/^(?:cat|dog)$/It keeps the result free of captures you do not use. This also makes the pattern a bit faster.
Named capturing groups
Names make larger patterns easier to read:
const match = '2026-08-29'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/)
console.log(match?.groups)
// { year: '2026', month: '08', day: '29' }Backreferences
A backreference matches the same text as an earlier group. \1 refers to the
first capture:
console.log(/\b(\w+)\s+\1\b/i.test('the the')) // true
console.log(/\b(\w+)\s+\1\b/i.test('the cat')) // falseNamed groups use \k<name>:
console.log(/(?<quote>['"]).*?\k<quote>/.test('Say "hello"')) // trueLookahead and lookbehind
Lookarounds require surrounding text without including it in the match.
| Pattern | Meaning |
|---|---|
x(?=y) | Match x followed by y |
x(?!y) | Match x not followed by y |
(?<=y)x | Match x preceded by y |
(?<!y)x | Match x not preceded by y |
For example, match the number after a dollar sign without matching the sign:
'Total: $42'.match(/(?<=\$)\d+/)?.[0]
// 42Lookbehind is less portable in old JavaScript runtimes. A capture often does the same job:
'Total: $42'.match(/\$(\d+)/)?.[1]
// 42Read more: Match text between two markers with regex.
Regex flags
Flags change how a pattern runs. Put them after a literal or pass them as the second constructor argument.
| Flag | Name | Effect |
|---|---|---|
d | Indices | Adds start and end indices for matches and captures |
g | Global | Finds successive matches and updates lastIndex |
i | Ignore case | Matches without case sensitivity |
m | Multiline | Makes ^ and $ work at line boundaries |
s | Dot all | Lets . match line terminators |
u | Unicode | Uses Unicode-aware parsing and code points |
v | Unicode sets | Adds richer Unicode-aware character classes |
y | Sticky | Matches only at lastIndex |
The u and v flags cannot be used together.
Common combinations are /word/gi for every case-insensitive match and
/^value$/m for whole lines.
The g flag does not always mean "return every match." Each string method has
its own return shape. The next section shows the difference.
Using regex with JavaScript
Check a match with .test()
RegExp.prototype.test() returns a boolean:
const isSecureUrl = /^https:\/\//.test('https://example.com')
console.log(isSecureUrl) // trueAvoid reusing a global or sticky regex for independent test() calls. Those
flags update lastIndex, so repeated calls can produce surprising results.
Find a position with .search()
String.prototype.search() returns the first match's index, or -1:
console.log('bad unboxing'.search(/box/)) // 6
console.log('bad unboxing'.search(/duck/)) // -1Get one match with .match()
Without g, String.prototype.match() returns the full match, captures, and
extra metadata:
const match = 'Name: Ada'.match(/Name: (\w+)/)
console.log(match?.[0]) // Name: Ada
console.log(match?.[1]) // Ada
console.log(match?.index) // 0With g, it returns all full matches but drops the captures:
const text = 'Tags: {{first}} and {{second}}'
console.log(text.match(/\{\{\w+\}\}/g))
// ['{{first}}', '{{second}}']It returns null when nothing matches.
Get every match with .matchAll()
String.prototype.matchAll() keeps the captures for every match. The regex must
have the g flag:
const text = 'Tags: {{first}} and {{second}}'
const matches = text.matchAll(/\{\{(\w+)\}\}/g)
console.log([...matches].map(match => match[1]))
// ['first', 'second']matchAll() returns an iterator. Spread it, use Array.from(), or loop over it
with for...of.
Replace matches with .replace()
String.prototype.replace() accepts a replacement string or function:
const text = 'Good morning, morning people'
console.log(text.replace(/morning/g, 'evening'))
// Good evening, evening peopleThe function receives the full match, captures, offset, original string, and named groups:
const values = { fruitCount: 5, veggieCount: 7 }
const text = '{{fruitCount}} fruit and {{veggieCount}} vegetables'
const result = text.replace(/\{\{(\w+)\}\}/g, (match, key) => {
return Object.hasOwn(values, key) ? values[key] : match
})
console.log(result)
// 5 fruit and 7 vegetablesWithout g, replace() changes only the first match. replaceAll() is another
option, but a regex passed to it must also have g.
Split on a pattern with .split()
String.prototype.split() can use a regex as its separator:
const words = 'one, two;three'.split(/[,;]\s*/)
console.log(words)
// ['one', 'two', 'three']If the separator contains capturing groups, their matches are included in the
result. Use (?:) when you do not want that.
Read successive matches with .exec()
RegExp.prototype.exec() returns one match at a time. A global or sticky regex
stores the next starting position in lastIndex:
const pattern = /\b\w+\b/g
const text = 'one two'
console.log(pattern.exec(text)?.[0]) // one
console.log(pattern.lastIndex) // 3
console.log(pattern.exec(text)?.[0]) // two
console.log(pattern.lastIndex) // 7Prefer matchAll() when you simply need every match. Use exec() when its
stateful control is useful.
Using variables safely
This pattern looks for the literal word needle, not the variable:
const needle = 'work'
console.log(/needle/.test('Will this work?')) // falseBuild a dynamic pattern with RegExp:
const needle = 'work'
const pattern = new RegExp(needle, 'i')
pattern.test('Will this work?') // trueEscape dynamic text before treating it as literal text. Otherwise input such as
a.b changes the meaning of the pattern:
const needle = 'a.b'
const pattern = new RegExp(RegExp.escape(needle), 'i')
pattern.test('Use a.b') // true
pattern.test('Use axb') // falseRegExp.escape() is the built-in choice in current JavaScript. Check runtime
support if you target older browsers or Node.js versions.
Practical patterns
Match a whole ASCII word
const word = 'cat'
const pattern = new RegExp(`\\b${RegExp.escape(word)}\\b`, 'i')
pattern.test('A cat naps') // true
pattern.test('A scatter plot') // falseMatch a CSS hexadecimal color
CSS hex colors can contain three, four, six, or eight hexadecimal digits:
const hexColor = /^#(?:[\da-f]{3,4}|[\da-f]{6}(?:[\da-f]{2})?)$/i
hexColor.test('#09f') // true
hexColor.test('#0099ffaa') // true
hexColor.test('#xyz') // falseMatch an MD5-shaped string
An MD5 digest is 32 hexadecimal characters:
const md5 = /^[\da-f]{32}$/i
md5.test('7e18a1b53182d6124253453811b67eb0') // trueThis checks the format. It cannot prove how the value was produced.
Extract placeholders
const text = 'Hello, {{first_name}} {{last_name}}'
const placeholder = /\{\{(?<key>[A-Za-z_]\w*)\}\}/g
const keys = [...text.matchAll(placeholder)].map(match => match.groups?.key)
console.log(keys)
// ['first_name', 'last_name']Check a date format
const isoDate =
/^(?<year>\d{4})-(?<month>0[1-9]|1[0-2])-(?<day>0[1-9]|[12]\d|3[01])$/
isoDate.test('2026-08-29') // true
isoDate.test('29-08-2026') // falseThis checks the shape and broad ranges. It still accepts impossible dates such
as 2026-02-31. Use a date API for calendar validation.
Check a known web origin
A regex can check a narrow URL shape:
const siteUrl = /^https:\/\/example\.com(?:[/?#]|$)/i
siteUrl.test('https://example.com/docs') // true
siteUrl.test('https://example.com.evil.test') // falseFor real URL parsing, use the URL class:
const url = new URL('/docs', 'https://example.com')
const isInternal = url.origin === 'https://example.com'
console.log(isInternal) // trueDo not use a giant regex to parse every valid URL, email address, programming language, or HTML document. Use the parser made for that format.
Common mistakes
Forgetting anchors
/\d{4}/ finds four digits anywhere. /^\d{4}$/ requires exactly four digits.
Repeating the wrong token
/hello*/ repeats only o. /(?:hello)*/ repeats hello.
Forgetting to escape a dot
example.com matches exampleXcom. example\.com matches the literal domain.
Adding g everywhere
Flags affect method behavior. match() loses captures with g, and a reused
global test() pattern changes its starting position.
Using \w for every language
\w is mostly ASCII. Use Unicode property escapes such as \p{Letter} with u
or v when the input is multilingual.
Injecting raw text into RegExp
Dynamic text may contain metacharacters. Pass it through RegExp.escape() when
it should be literal.
Solving structure with a text pattern
Regex is excellent for local text patterns. It is a poor substitute for an HTML, URL, JSON, or language parser.
Quick reference
| Syntax | Meaning |
|---|---|
. | Any character except a line terminator, unless s is used |
[abc] | One of a, b, or c |
[^abc] | One character except a, b, or c |
\d / \D | Digit / non-digit |
\w / \W | ASCII word / non-word character |
\s / \S | Whitespace / non-whitespace |
^ / $ | Start / end anchor |
\b / \B | Word boundary / non-boundary |
x? | Zero or one x |
x* | Zero or more x |
x+ | One or more x |
x{2,4} | Two to four x characters |
x|y | x or y |
(x) | Capture x |
(?:x) | Group x without capturing |
(?<name>x) | Capture x as name |
\1 | Repeat the first capture |
x(?=y) | x followed by y |
(?<=y)x | x preceded by y |
Testing regex
Test patterns against examples that should match and examples that should not. regex101 is useful, but select its JavaScript flavor. For exact runtime behavior, write a small JavaScript test.
Keep patterns narrow. Name complex pieces in prose or code. If a regex takes longer to explain than the format itself, a parser may be clearer.
For more detail, see the MDN regular expressions guide and the MDN regex reference.