# A beginner-friendly guide to regex in JavaScript

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`:

```regex
/hello/
```

Use it with a JavaScript string method:

```js
const greeting = 'Well, hello'

console.log(greeting.replace(/hello/, 'hi'))
// Well, hi
```

Regex 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:

```js
const pattern = /hello/gi
```

Literals 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:

```js
const word = 'hello'
const pattern = new RegExp(word, 'gi')
```

The first argument is a string. Backslashes inside it must be escaped:

```js
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:

```text
. ^ $ * + ? ( ) [ ] { } | \
```

Add a backslash to match one literally:

```regex
/\?/
```

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:

```js
const protocol = /https:\/\//
```

You do not need to escape `/` in a constructor string:

```js
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:

```regex
/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:

```js
console.log(/start.end/s.test('start\nend')) // true
```

### Character classes `[]`

A character class matches one character from a set:

```regex
/[aeiou]/
```

Ranges keep classes short:

```regex
/[0-9]/
/[a-z]/
/[A-Fa-f0-9]/
```

The last pattern matches one hexadecimal character.

Put `^` first to negate a class:

```regex
/[^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:

```regex
/^[^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:

```js
console.log(/^\p{Letter}+$/u.test('München')) // true
```

Unicode 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:

```js
console.log(/^cat$/.test('cat')) // true
console.log(/^cat$/.test('a cat')) // false
```

Use word boundaries to avoid partial words:

```js
console.log(/\bcat\b/.test('a cat naps')) // true
console.log(/\bcat\b/.test('scatter')) // false
```

Word 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:

```regex
/colou?r/
```

It matches `color` and `colour`.

The star allows zero matches:

```js
console.log(/^\d*$/.test('')) // true
console.log(/^\d*$/.test('123')) // true
```

The plus requires at least one:

```js
console.log(/^\d+$/.test('')) // false
console.log(/^\d+$/.test('123')) // true
```

Quantifiers 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:

```js
'<b>one</b><i>two</i>'.match(/<.+>/)
// ['<b>one</b><i>two</i>']
```

Add `?` after a quantifier to make it lazy:

```js
'<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":

```regex
/cat|dog/
```

Alternation has low precedence. Group it when anchors or quantifiers should
apply to every option:

```js
console.log(/^(?:cat|dog)$/.test('dog')) // true
console.log(/^(?:cat|dog)$/.test('hotdog')) // false
```

### Capturing groups `()`

Parentheses group a pattern and capture its match:

```js
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]) // 29
```

Captured values can be used in replacements:

```js
const date = '2026-08-29'

console.log(date.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1'))
// 29/08/2026
```

### Non-capturing groups `(?:)`

Use a non-capturing group when you only need grouping:

```regex
/^(?: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:

```js
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:

```js
console.log(/\b(\w+)\s+\1\b/i.test('the the')) // true
console.log(/\b(\w+)\s+\1\b/i.test('the cat')) // false
```

Named groups use `\k<name>`:

```js
console.log(/(?<quote>['"]).*?\k<quote>/.test('Say "hello"')) // true
```

## Lookahead 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:

```js
'Total: $42'.match(/(?<=\$)\d+/)?.[0]
// 42
```

Lookbehind is less portable in old JavaScript runtimes. A capture often does the
same job:

```js
'Total: $42'.match(/\$(\d+)/)?.[1]
// 42
```

Read more: [Match text between two markers with regex](/match-in-between).

## 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:

```js
const isSecureUrl = /^https:\/\//.test('https://example.com')

console.log(isSecureUrl) // true
```

Avoid 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`:

```js
console.log('bad unboxing'.search(/box/)) // 6
console.log('bad unboxing'.search(/duck/)) // -1
```

### Get one match with `.match()`

Without `g`, `String.prototype.match()` returns the full match, captures, and
extra metadata:

```js
const match = 'Name: Ada'.match(/Name: (\w+)/)

console.log(match?.[0]) // Name: Ada
console.log(match?.[1]) // Ada
console.log(match?.index) // 0
```

With `g`, it returns all full matches but drops the captures:

```js
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:

```js
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:

```js
const text = 'Good morning, morning people'

console.log(text.replace(/morning/g, 'evening'))
// Good evening, evening people
```

The function receives the full match, captures, offset, original string, and
named groups:

```js
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 vegetables
```

Without `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:

```js
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`:

```js
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) // 7
```

Prefer `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:

```js
const needle = 'work'

console.log(/needle/.test('Will this work?')) // false
```

Build a dynamic pattern with `RegExp`:

```js
const needle = 'work'
const pattern = new RegExp(needle, 'i')

pattern.test('Will this work?') // true
```

Escape dynamic text before treating it as literal text. Otherwise input such as
`a.b` changes the meaning of the pattern:

```js
const needle = 'a.b'
const pattern = new RegExp(RegExp.escape(needle), 'i')

pattern.test('Use a.b') // true
pattern.test('Use axb') // false
```

`RegExp.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

```js
const word = 'cat'
const pattern = new RegExp(`\\b${RegExp.escape(word)}\\b`, 'i')

pattern.test('A cat naps') // true
pattern.test('A scatter plot') // false
```

### Match a CSS hexadecimal color

CSS hex colors can contain three, four, six, or eight hexadecimal digits:

```js
const hexColor = /^#(?:[\da-f]{3,4}|[\da-f]{6}(?:[\da-f]{2})?)$/i

hexColor.test('#09f') // true
hexColor.test('#0099ffaa') // true
hexColor.test('#xyz') // false
```

### Match an MD5-shaped string

An MD5 digest is 32 hexadecimal characters:

```js
const md5 = /^[\da-f]{32}$/i

md5.test('7e18a1b53182d6124253453811b67eb0') // true
```

This checks the format. It cannot prove how the value was produced.

### Extract placeholders

```js
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

```js
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') // false
```

This 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:

```js
const siteUrl = /^https:\/\/example\.com(?:[/?#]|$)/i

siteUrl.test('https://example.com/docs') // true
siteUrl.test('https://example.com.evil.test') // false
```

For real URL parsing, use the `URL` class:

```js
const url = new URL('/docs', 'https://example.com')
const isInternal = url.origin === 'https://example.com'

console.log(isInternal) // true
```

Do 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](https://regex101.com/) 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions)
and the
[MDN regex reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions).
