Local browser utility · Examples and limits included

Regex Tester

Test JavaScript regular expressions against your own text, with every match, numbered and named capture group, and a live replacement preview.

Flags

This runs the browser’s own engine, so the flavour is JavaScript (ECMAScript) — not PCRE. Atomic groups, possessive quantifiers, recursion and /x mode do not exist here.

Test string

1 line · 0 characters

Matches

Replace (optional)

Use $& for the whole match, $1 and $<name> for capture groups, $` for the text before it and $' for the text after.

Output

Matched locally in your browser No data is sent to any server

What the result contains

What This Tool Provides

Test a regular expression against your own text and see exactly what it matched: every match with its position, every numbered and named capture group, and a live preview of what a replacement would produce. The engine is the browser’s own, so the flavour is JavaScript — which is not PCRE, and the difference is where most copied patterns go wrong.

  • Every match with its index in the subject text
  • Numbered and named capture groups, listed per match
  • A clear distinction between an empty match and a group that did not participate
  • A live replacement preview using $1 and $<name> syntax

How to Use This Tool

  1. Write the patternType the expression without surrounding slashes. Set flags with the checkboxes rather than writing /pattern/gi.
  2. Paste the test textAdd the subject you want to match against. Results update as you type in either field.
  3. Read the groupsEach match lists its capture groups by number, or by name when the pattern declares them, so you can see which part of the pattern caught what.

How It Works

The pattern and the selected flags are handed to the browser’s RegExp constructor, and matches are collected by calling exec in a loop until it returns null. That loop carries one guard the naive version lacks: a pattern that matches the empty string never advances lastIndex on its own, so the position is nudged forward by one after any zero-length match. Without it, a pattern such as a* would spin forever on the first character. Collection stops at 1,000 matches so a pattern matching every position in a long document cannot lock the page while it renders results nobody will scroll through.

Worked Examples

Named groups from an email-shaped string

Input
Pattern (?<user>\w+)@(?<domain>[\w.]+) · flags g · text "write to sam@example.com today"
Output
Match 1 at index 9: sam@example.com $<user> = sam $<domain> = example.com

Named groups are reported by name rather than as $1 and $2, which is what the $<name> replacement syntax refers to.

A group that did not participate

Input
Pattern (cat)|(dog) · text "dog"
Output
Match 1 at index 0: dog $1 = (did not participate) $2 = dog

The first alternative never ran, so $1 is undefined rather than empty. Treating those as the same value is a common source of null errors downstream.

The dot does not cross a newline by default

Input
Pattern a.b · text "a\nb"
Output
No matches. Adding the s flag produces one match.

In JavaScript the dot excludes line terminators unless the dotAll flag is set. Patterns moved from a language where the dot matches everything fail here silently.

This Is JavaScript, Not PCRE

The single most useful thing to know about any regex tool is which engine sits behind it, because the syntax that works in one flavour can throw, or quietly match something different, in another. This page runs your pattern through the browser’s own RegExp implementation, which means ECMAScript rules. A pattern copied from a PHP, Perl, Python or Java codebase is not guaranteed to behave the same way here, and neither is one you test here and then deploy into a PCRE-based tool such as grep -P or a Nginx config.

Some of the differences produce an immediate error, which is the good case: you find out at once. Others are silent, and those are the ones worth memorising. The dot excluding newlines unless you set the dotAll flag is silent. So is the fact that JavaScript has no /x extended mode, so whitespace and # comments inside a pattern are matched literally rather than ignored — a multi-line commented pattern pasted from Python will match nothing at all and report no error.

The table below lists what exists elsewhere and does not exist here. When you need one of them, the honest answer is that the browser cannot do it and you should test in the environment that will actually run the pattern.

Common PCRE features and their status in JavaScript
FeaturePCRE syntaxIn JavaScript
Atomic group(?>…)Not supported — throws
Possessive quantifiera++Not supported — throws
Recursion / subroutine call(?R), (?1)Not supported — no equivalent
Extended whitespace mode/xNo such flag; spaces and # are literal
Start / end of subject anchors\A, \zUse ^ and $ without the m flag
Lookbehind(?<=…)Supported in all current browsers
Named group(?P<name>…)Written (?<name>…) instead
Unicode property\p{L}Supported, but only with the u flag

What Each Flag Actually Changes

Flags are set with checkboxes here rather than typed as /pattern/gi, because the slash-delimited form is a literal syntax from JavaScript source code, not part of the pattern itself. Pasting the slashes into the pattern box makes them characters to match.

Global is the one people miss most often. Without it, the tool reports a single match and stops, which looks like a broken pattern when it is actually correct. Multiline is the second: it changes ^ and $ from meaning start and end of the whole subject to start and end of each line, which is what you want when testing against a log file and not what you want when validating a single value. Sticky is rarely needed outside tokenizers, where each match has to begin exactly where the previous one ended. When a flag change alters the replacement output in a way you did not expect, diffing the before and after text shows precisely which occurrences moved, which is quicker than rereading both versions.

The unicode flag deserves special mention because it is not merely additive. With it, the pattern is interpreted as code points rather than UTF-16 units, \p{…} property escapes become available, and several previously tolerated escapes become syntax errors. That last part is why adding u to an old pattern sometimes breaks it: the pattern was always slightly wrong and the engine was being permissive.

Catastrophic Backtracking, and Why This Page Has Limits

A regular expression can take exponential time on an input that is only slightly longer than one it handled instantly. The classic shape is nested quantifiers over overlapping alternatives — something like (a+)+$ tested against a long run of a characters followed by anything else. The engine tries every possible division of the string between the inner and outer quantifier before concluding there is no match, and the number of divisions doubles with each added character.

This matters here for an honest reason: nothing in this page can stop it. A JavaScript regex match runs to completion on the main thread and cannot be interrupted or timed out. What the tool can do is reduce the odds and bound the damage, which is why the subject is capped at 200,000 characters and match collection stops at 1,000. If you paste a pattern of this shape against a long subject and the tab stops responding, that is the engine, not a crash — closing the tab is the only remedy, and the pattern needs rewriting before it goes anywhere near a server.

The practical defence is structural: prefer a specific character class to a dot, avoid a quantified group that contains another quantifier, and anchor the pattern so it fails fast. A pattern that is safe on your laptop against 50 characters is not evidence of anything about a 5,000-character request body.

When a Regex Is the Wrong Tool

Regular expressions describe regular languages, and quite a lot of what people point them at is not one. HTML and XML nest arbitrarily deep, so no expression can match a tag and its matching close tag in general. JSON has the same problem: you can pull a value out of a flat, known-shaped payload with a pattern, and it will work until the day a nested object appears. Formatting and validating the JSON and reading the structure is the approach that keeps working.

HTML is the textbook example. It nests arbitrarily deep, so no expression can match a tag together with its own closing tag in the general case — which is why converting it with a real parser works and a pattern eventually will not.

CSV is the case that catches people most often, because it looks flat. It is not: a field may be quoted, and a quoted field may contain the delimiter or a line break, so splitting rows on commas corrupts data silently rather than failing. Converting the CSV properly handles the quoting rules and reports the rows it could not make sense of.

Encoding is the quiet version of the same mistake. A pattern written for readable text will not match that text once it has been percent-escaped or Base64-wrapped, and the failure looks like a broken pattern rather than an encoding problem. If the subject arrived from a query string, decode the percent-escapes first; if it arrived as an opaque blob, decode the Base64 before trying to match anything inside it.

The other frequent misuse is validation of things with real specifications. Email addresses are the standing example — the grammar in RFC 5322 is not expressible as a sane pattern, and the elaborate ones circulating online reject valid addresses. Password composition rules are a related case: enforcing a pattern of required character classes is now advised against, because it pushes people toward predictable substitutions rather than length. Generating a long random secret is the better answer than validating a short one against a clever pattern.

Important Edge Cases

  • A pattern that can match the empty string produces a match at every position, including one past the final character. The tool advances by one each time so it terminates rather than hanging.
  • A group inside an alternative that never ran is undefined, which is reported as “did not participate” and is different from a group that matched an empty string.
  • Writing /pattern/flags in the pattern box matches literal slash characters. Set flags with the checkboxes instead.
  • Adding the u flag to an older pattern can turn a previously tolerated escape into a syntax error, because unicode mode is stricter rather than merely broader.
  • Backreferences such as \1 refer to captured text, not to the pattern that captured it, so (a|b)\1 matches "aa" and "bb" but never "ab".

Limitations

  • The flavour is JavaScript. Atomic groups, possessive quantifiers, recursion, and /x extended mode do not exist and will throw or match literally.
  • A match cannot be interrupted once started, so a catastrophically backtracking pattern can hang the tab despite the input caps.
  • Subject text is capped at 200,000 characters and match collection stops at 1,000 matches.
  • The tool does not explain a pattern in words or diagram it; it shows what the pattern did against the text you supplied.
  • Testing here proves the pattern works in a browser, not in grep, PHP, Python, or a database regex function, each of which uses a different engine.

Input handling

Private Browser Processing

The pattern and the test text stay in the page. Matching runs in your browser through its own RegExp engine, nothing is sent to a server, and neither the pattern nor the subject is stored or logged.

Google Analytics records page visits and whether a tool finished or failed, but these tool implementations do not send field values, generated passwords, QR payloads, uploaded QR images, or camera frames to analytics or a Tiny Tiny Tools processing endpoint.

See the Privacy Policy for site-wide details.

Questions About This Tool

Which regex flavour does this use?

JavaScript, also called ECMAScript, because the matching is done by the browser you are reading this in. That differs from PCRE in ways that matter: no atomic groups, no possessive quantifiers, no recursion, and no /x mode. A pattern verified here is verified for JavaScript specifically.

Why does my pattern only find the first match?

The global flag is off. Without g, the engine stops at the first match by design. Tick the g checkbox to collect every match.

Why does a group show “did not participate” instead of empty?

Because the two are genuinely different. A group in an alternative that never ran is undefined; a group that matched zero characters is an empty string. Code that treats them the same is where undefined errors come from.

Do I include the slashes around my pattern?

No. The slashes are JavaScript source syntax for writing a regex literal, not part of the expression. Typing them makes them characters to match. Use the flag checkboxes for the trailing g, i and friends.

Is my test text sent anywhere?

No. The pattern and the text are matched by your own browser. There is no request, and you can confirm that in the network tab.

Can I use this to validate email addresses?

You can, but the result will be worse than you expect. The real grammar is far more permissive than the patterns circulating online, which reject valid addresses. Checking for an @ with something either side, then sending a confirmation message, is the approach that actually works.