Regex Tester & Builder

Test a regular expression against sample text in your browser, see every match, position and capture group instantly. Nothing ever leaves your device.

🌐 Español

🔒 Private by design: your text is processed locally in your browser and never uploaded to any server.

Two boxes, one pattern, one sample

This page sits on the same two-textarea layout as Diff Text, which is why the boxes carry generic labels. The box labeled “Original text” holds the pattern. The box labeled “Changed text” holds the sample you want to run it against. Paste the pattern bare, without the slashes a JavaScript literal would need, so \d{4} rather than the slash-wrapped form.

Dropping the slash notation was a deliberate call when this tool was built. Slash delimiters look familiar, but they break on exactly the patterns people test most often: a URL path, a Windows or POSIX file path, a date written as \d{4}/\d{2}/\d{2}. Every one of those contains an unescaped forward slash, and a parser that split on the last slash to find the flags would either mangle the pattern or force you to escape characters that need no escaping. A bare pattern plus four flag checkboxes has no such trap, and the checkboxes double as documentation of what each flag does.

One small detail worth knowing: only leading and trailing newline characters are stripped from the pattern box, never spaces. A pattern that genuinely ends in a space, like error when you are matching a log prefix, survives intact. Trimming it would quietly change what you asked to test.

Steps for a first test run

  1. Paste your pattern into the first box.
  2. Paste real sample lines into the second box, including one or two you expect not to match. A pattern that only ever sees passing input is not tested, it is merely confirmed.
  3. Set the flags. Leave “Find all matches” on unless you specifically want first-match behaviour.
  4. Click the button and read the report below.

Reading the report: matches, indexes and capture groups

The output is one plain-text report, built fresh on every click. It opens by echoing the pattern back in /pattern/flags form, so you can confirm at a glance that the flags you thought you set are the flags in play. Then comes a one-line description of each active flag, a validity status, a total match count, and one block per match.

Each block gives the matched text, the index range it occupies, every numbered capture group, and every named group if the pattern uses the (?<name>...) syntax. Groups are where most real debugging happens. A group that participated but captured nothing is printed as (did not match) rather than as an empty pair of quotes, and that distinction is usually the answer to “why did my script get undefined instead of a value”. An optional group that never fired and a group that matched an empty string look identical in most editors; here they do not.

Underneath the match list, your sample text is reprinted whole with every match bracketed as »matched«, left to right, non-overlapping. Reading that block is faster than mentally applying index arithmetic to a list of numbers.

The four flags, and which one usually explains the surprise

g decides whether you get every match or only the first. It is on by default because that is what testing against a sample almost always means, and turning it off is mainly useful when you want to reproduce how a non-global pattern behaves inside real code.

i makes the whole pattern case-insensitive. m changes what ^ and $ mean: with it on they anchor to the start and end of each line, with it off they anchor to the start and end of the entire text. Multi-line samples are where a pattern most often looks broken for no visible reason, and this checkbox is usually the fix.

s is the one people forget. By default the dot does not match a newline character, so a pattern like start.*end will never span two lines no matter how many quantifiers you add. Turn on the s checkbox and the dot covers newlines too. If you are trying to pull a multi-line block out of a config file or a stack trace, start there.

Zero-length matches and the empty marker pairs

Matches are collected with String.prototype.matchAll when the g flag is on, rather than a hand-rolled loop around exec. That is not a stylistic preference. A manual loop has to notice zero-length matches itself and nudge the cursor forward, and forgetting to do so is the classic way to hang a browser tab on a pattern like a*. matchAll handles the advance per the language spec, which is why a pattern that can match nothing still terminates here instead of spinning.

The visible consequence is that patterns able to match an empty string produce a lot of »« pairs with nothing between them. Those are genuine zero-length matches at genuine positions, counted in the total. They are not filtered out, because hiding them would make the match count and the index list disagree with what the pattern actually did.

What it will not do, and where your sample text goes

There is no token-by-token plain-English explainer here. Walking a pattern and narrating every construct is a large feature with a lot of room to be subtly wrong, and a wrong explanation of a regex is worse than none. What you get instead is an unambiguous test report: valid or not, which flags, how many matches, where, and what each group captured.

The sample text you paste is rarely synthetic. It is a real log line with a customer address in it, a copied support ticket, a chunk of an API response, a key format you would rather not publish. Nothing here is transmitted: the browser’s own RegExp object is a complete regex engine, no library is loaded for it, and the report is assembled locally the moment you click. Once the page has loaded you can go offline and it still works exactly the same. If you want to double-check the structure of that sample first, JSON Formatter and Case Converter run the same way.

See it in action

Screenshot of the Regex Tester & Builder tool with the sample input “\b[\w.+-]+@[\w-]+\.[\w.]+\b”, Find all matches (g flag), off finds only the first match set to on, Case-insensitive (i flag) set to off
Regex Tester & Builder mid-process: the sample input “\b[\w.+-]+@[\w-]+\.[\w.]+\b”, Find all matches (g flag), off finds only the first match set to on, Case-insensitive (i flag) set to off.
Screenshot of the Regex Tester & Builder result screen showing the generated output “=== REGEX TEST RESULTS === Pattern: /\b[\w.+-]+@…”
The finished result: the generated output “=== REGEX TEST RESULTS === Pattern: /\b[\w.+-]+@…”. The download link is a local blob URL — the file never leaves your device.

Frequently asked questions

Which box takes the pattern and which one takes the sample text?

The first box, labeled "Original text", takes the pattern. The second box, labeled "Changed text", takes the text you want to match against. Those labels are generic because this page reuses the site's two-textarea comparison layout, and the pattern goes in bare, so `\d+` rather than `/\d+/`.

Why does a pattern that works in Python or grep fail here?

The engine is the JavaScript `RegExp` built into your browser, so the dialect is JavaScript's. Character classes, quantifiers, lookahead and modern lookbehind all behave as you would expect, but constructs that only exist in PCRE or Python (inline flag groups like `(?i)`, atomic groups, possessive quantifiers, recursion) are not part of the JavaScript grammar and will be reported as a syntax error. Watch out for `\A` and `\Z` in particular: they are not errors here, they are identity escapes, so they silently match a literal `A` and a literal `Z` instead of anchoring anything.

What do the » and « marks around parts of my text mean?

They bracket exactly the characters that matched, so a four-digit-year pattern against a log line shows »2026« and nothing more. Anything outside those marks was skipped by your pattern. It is the closest this plain-text report can get to the coloured highlighting a code editor would give you.

Why do I see lots of empty »« pairs with no text inside?

Your pattern can match an empty string, which is what a trailing star or a bare word boundary does. A zero-length match is a real match at a real position, so it is counted and marked like any other. If you did not intend it, tighten the quantifier (`a+` instead of `a*`) and the empty pairs disappear.

Can I test a replacement string as well as the pattern?

No. This tool reports matches, positions and capture-group values, and it deliberately stops there; there is no replacement field and no preview of substituted output. Confirm the pattern selects exactly the right spans here first, then run your actual replace in your editor, script or `sed` command with confidence that the pattern half is correct.

What happens if the pattern itself is invalid?

The report prints the browser's own error text, for example "Invalid regular expression: /(abc/g: Unterminated group", flags included because the pattern is always compiled with them, instead of a vague failure message. Nothing crashes and nothing is lost from either box, so you can fix the pattern and click again.

Related tools