Skip to content
DevPenh

Regex Tester

Test a regular expression against your own text and see every match highlighted, with its position and capture groups. Preview a replace before you put it in code. Patterns run on your browser's JavaScript engine, so the results match what RegExp, match and replace do in your app.

Flags
0 / 200,000 characters

Type a pattern and some test text, or load an example. Matches update as you type.

Replace

How to use the regex tester

  1. Type the pattern without the surrounding slashes, for example \d+, not /\d+/.
  2. Tick the flags you need. g is on by default so you see every match.
  3. Paste the test text. Matches are highlighted and listed with their line, column, index range and capture groups.
  4. Type a replacement to preview the result of text.replace(regex, replacement).

Copy as /regex/ gives a literal you can paste into JavaScript or TypeScript, with any / escaped. Use Copy pattern when you need the bare pattern, and remember that inside a normal string every backslash must be doubled: new RegExp("\\d+", "g").

Regex syntax quick reference

This tester uses JavaScript (ECMAScript) regular expressions. The table covers the parts you will use most often.

SyntaxMeaning
.Any character except a line break (any character with the s flag)
\d \w \sA digit, a word character [A-Za-z0-9_], whitespace
\D \W \SThe opposite of each of the above
[abc] [a-z] [^0-9]One character from a set or range; ^ negates the set
\p{L} \p{N} \p{M}A Unicode letter, number or mark (needs the u flag)
* + ?Zero or more, one or more, zero or one of the previous item
{3} {2,} {2,5}Exactly 3, at least 2, between 2 and 5
*? +? {2,5}?Lazy versions: match as little as possible
^ $Start and end of the text (of each line with the m flag)
\b \BA word boundary, and anything that is not one
(…)A capture group, numbered from 1 by its opening parenthesis
(?<name>…)A named capture group
(?:…)A group that does not capture
\1 \k<name>A backreference: the same text a group matched
a|bEither side of the bar
(?=…) (?!…)Lookahead: followed by, or not followed by
(?<=…) (?<!…)Lookbehind: preceded by, or not preceded by

To match a character that has a special meaning, such as . * + ? ( ) [ ] { } ^ $ | \ /, put a backslash in front of it: \. matches a literal full stop.

Flags

  • g global finds every match. Without it, only the first match is found and replace changes only the first one.
  • i ignore case makes a and A equal.
  • m multiline makes ^ and $ match at every line, not only at the start and end of the whole text.
  • s dotAll lets . match line breaks too.
  • u unicode treats emoji and other characters outside the Basic Multilingual Plane as one character, enables \p{…} classes, and turns unknown escapes such as \- outside a class into errors.
  • y sticky only matches where the previous match ended, which is useful for tokenizers.

JavaScript also has d (match indices) and v (an extended form of u). The tester does not offer them as checkboxes: d does not change what matches, and v was only added to browsers in 2023, so older ones reject it.

Practical examples

Each example can be loaded into the tester with Load an example. The matches shown here are checked by the site's automated tests.

Email address shape

/^[^\s@]+@[^\s@]+\.[^\s@]+$/gm

Matches: "ada@example.com", "bob@mail.co.uk".

Checks for something@something.something on each line. It is a shape check, not validation: the only reliable test is sending a confirmation email.

ISO dates with named groups

/\b(?<year>\d{4})-(?<month>0[1-9]|1[0-2])-(?<day>0[1-9]|[12]\d|3[01])\b/g

Matches: "2026-10-23", "2027-02-31".

Rejects month 13 but still accepts 31 February. Parse the date and check it round-trips if the calendar matters.

URLs in text

/\bhttps?:\/\/[^\s<>"']+/g

Matches: "https://example.com/guide?q=1", "http://localhost:3000/api.".

Finds links in prose, but keeps a trailing full stop. Use the URL constructor to validate a single URL instead of a regex.

Repeated spaces and tabs

/[ \t]{2,}/g

Matches: " ", " \t". Replacing with " " gives "name: Ada\tLovelace role: admin".

Collapses runs of two or more spaces or tabs into one space. A single tab is left alone because it is not a run.

Integers and decimals

/-?\d+(?:\.\d+)?/g

Matches: "-3.5", "82", "1", "200".

Shows a common surprise: thousands separators split 1,200 into two numbers. Decide on the number format first, then write the pattern.

Error and warning log lines

/^(?<time>\d{2}:\d{2}:\d{2}) (?<level>ERROR|WARN) (?<message>.*)$/gm

Matches: "09:15:07 WARN slow query (812 ms)", "09:16:11 ERROR payment timeout".

The m flag makes ^ and $ work per line. Without it, ^ only matches at the very start of the text and nothing is found.

Duplicated words

/\b(\w+)\s+\1\b/gi

Matches: "is is", "The the".

\1 is a backreference to the first group. With the i flag, The and the count as the same word.

CSS hex colours

/#(?:[0-9a-f]{3}){1,2}\b/gi

Matches: "#FFF", "#1a2b3c".

Matches 3- or 6-digit colours. #12345 is skipped because \b requires the word to end after 3 or 6 digits.

Words in any script

/[\p{L}\p{M}]+/gu

Matches: "café", "Phnom", "Penh", "ភ្នំពេញ".

\p{…} needs the u flag. \p{L} alone splits Khmer, Thai or Hindi words because their vowel signs are marks (\p{M}), not letters.

Reformat dates with a replacement

/(\d{4})-(\d{2})-(\d{2})/g

Matches: "2026-10-23", "2026-11-02". Replacing with "$3/$2/$1" gives "Due 23/10/2026, paid 02/11/2026".

$1, $2 and $3 insert the captured groups. Named groups can be inserted with $<name>.

When a regex is not enough

A regex checks the shape of text, not its meaning. It cannot tell whether an email inbox exists, whether 2027-02-31 is a real date or whether a URL responds. Use a parser for structured formats: new URL() for URLs, JSON.parse for JSON (the JSON Formatter shows exactly where it fails), and a date library or Date round-trip for dates. Never parse HTML with a regex.

Using the result in JavaScript

const re = /(?<year>\d{4})-(?<month>\d{2})/g;
const text = "2026-10 and 2027-01";

for (const m of text.matchAll(re)) {
  console.log(m.index, m[0], m.groups.year);
}
// 0 2026-10 2026
// 12 2027-01 2027

text.replace(re, "$<month>/$<year>"); // "10/2026 and 01/2027"

matchAll requires the g flag and throws a TypeError without it. In the replacement string, $1 or $<name> inserts a group, $& the whole match, and $$ a literal dollar sign.

Common mistakes

  • Forgetting anchors. \d{4} matches inside 123456. Use ^\d{4}$ to require the whole input, or \b for whole words.
  • Greedy quantifiers. <.+> on <b>x</b> matches the whole string. Use the lazy <.+?> or a negated class <[^>]+>.
  • An unescaped dot. example.com also matches exampleXcom. Write example\.com.
  • Reusing a global regex with test(). A regex with g or y remembers lastIndex, so calling re.test() twice on the same string can return true then false. Drop the flag for test() or reset lastIndex to 0.
  • Single backslashes in strings. new RegExp("\d") is the pattern d, because the string drops the backslash. Write "\\d" or use a literal.
  • \w and \b are ASCII only. They do not treat é or Khmer letters as word characters, even with the u flag. Use [\p{L}\p{M}] with u.

Troubleshooting error messages

Errors come from your browser, so the wording differs a little between Chrome, Firefox and Safari. These are Chrome's.

  • Unterminated group: a ( has no matching ). Escape it as \( if you meant a literal bracket.
  • Nothing to repeat: a quantifier has nothing before it, as in *abc or a**. It also appears for possessive quantifiers like a++, which JavaScript does not support.
  • Invalid group: usually syntax from another flavor, such as atomic groups (?>…), or (?P<name>…) from Python. JavaScript uses (?<name>…).
  • Invalid escape: with the u flag, escapes that mean nothing (like \- outside a character class) are errors. Remove the backslash or the flag.
  • Range out of order in character class: a range such as [z-a] goes backwards. To include a literal -, put it first or last: [a-z-].
  • Duplicate capture group name: two groups have the same (?<name>…). Rename one.
  • Stopped after 2 seconds: this tester's own message, explained below.

Slow patterns and limits

Some patterns take exponential time when the text almost matches. The classic case is a repeated group that contains a repeat, such as (a+)+$ against thirty a characters followed by a b, which takes several seconds even on a fast computer. This is called catastrophic backtracking, and on a server it can be used for a denial-of-service attack (ReDoS).

A running JavaScript regex cannot be interrupted, so this tester runs it in a Web Worker and stops the worker after 2 seconds. The page stays usable, and you see a message instead of a frozen tab. The tester also warns when a pattern has the nested-repeat shape. That warning is a heuristic: it can miss slow patterns, such as overlapping alternatives like (a|a)*, and finishing within 2 seconds on your sample does not prove a pattern is safe on longer input.

Test text is limited to 200,000 characters. The first 1,000 matches are highlighted and listed (the table shows the first 200), while the replacement preview always covers every match.

What JavaScript regex does not support

Patterns copied from PCRE (PHP), Python, Java or .NET may need changes. JavaScript has no possessive quantifiers (a++), atomic groups ((?>…)), recursion, conditionals, \A / \Z anchors or the x (free-spacing) flag. Lookbehind, named groups, Unicode property escapes and inline modifiers such as (?i:…) are part of current JavaScript, but older browsers may lack the newest of them. Use ^ and $ without the m flag in place of \A and \z.

FAQ

Why does my regex only find one match?

The g flag is off. Without it, JavaScript stops at the first match, and replace changes only that one.

Why does my pattern work in Python or PHP but fail here?

Each language has its own regex flavor. Check the list of unsupported features above; named groups are the most common difference ((?P<name>…) in Python).

Why doesn't . match a new line?

By default . excludes line breaks. Turn on the s flag, or use [\s\S].

Can I validate an email address with a regex?

Only its rough shape. The full address grammar is too loose to be useful, and a valid-looking address may not exist. Check the shape, then send a confirmation email.

Do the match positions count characters or bytes?

The index range is in UTF-16 code units, exactly as match.index reports it, so an emoji counts as 2. The line and column count each emoji as one character.

Is my text sent anywhere?

No. Matching happens on your device, and nothing is saved when you leave the page. The privacy policy covers the rest of the site.

Related tools

Escape a value for a query string with the URL encoder before matching it in a URL, and use the JSON formatter to read structured log lines instead of picking them apart with a regex. Epoch numbers you find in logs can be read with the Unix timestamp converter.