🔧 TL3 Tools

🔍 Regex Tester

Written by Alex Chen · Reviewed by Jane Smith · July 20, 2026

Enter a regular expression pattern and a test string to see live match results, positions, and match counts.

Pattern

Flags

Test String

Results

Trying the classic phone-number pattern

Paste the anchored pair ^\d{3}-\d{2}-\d{4}$ into the Pattern field and type 123-45-6789 as the test string, then hit Test Regex. The caret pins the start of the string, \d{3} consumes exactly three digits, the hyphens match themselves literally, and the dollar sign demands the end of the input. Walking through it: the caret claims position 0, \d{3} swallows 123, the first hyphen matches, \d{2} takes 45, the second hyphen matches, and \d{4} takes 6789, leaving nothing for the dollar sign to object to. The result is one match spanning positions 0 through 11. Now change the test string to "Call 123-45-6789 today" and the same pattern reports nothing, because the caret refuses to start mid-sentence and the dollar sign refuses to end there. That contrast is the entire point of anchors, and it is the fastest way to see why a pattern that "should" match comes back empty.

Wrap each segment in parentheses — ^(\d{3})-(\d{2})-(\d{4})$ — and the results panel lists the captured groups beneath the match: $1 is 123, $2 is 45, $3 is 6789. That is how you pull the pieces out of a match instead of taking the whole string, and it is exactly the shape of code that extracts area codes or account numbers from free-form text.

What the four flags actually change

The g flag is switched on by default. With it, the tool loops through the text and reports every match; without it, the engine stops at the first one it finds. The i flag drops case sensitivity, so a pattern containing "abc" will happily match "ABC" in the test string. The m flag redefines the anchors: ^ and $ start matching at line boundaries instead of whole-string boundaries, which matters the moment your test string contains newlines. The s flag makes the dot match a newline character, something it refuses to do by default. The four checkboxes map directly onto the flag letters handed to the RegExp constructor, so what you verify here is exactly what you can paste into JavaScript code without translation.

How the engine walks your text

Under the hood this page builds a real RegExp object from the pattern and flags and runs its exec method against the test string. With the g flag it keeps calling exec until the call returns null, and it guards against a quirk where a zero-width match would otherwise spin forever by nudging lastIndex forward whenever a match consumes nothing. Each result reports the matched text, its 0-based starting position, the closing position, the numbered capture groups, and any named groups — all escaped before being written to the page so output can never be mistaken for markup. If the pattern itself is malformed, an unbalanced parenthesis or a stray quantifier, the engine throws and the red error box shows its message prefixed with "Regex Error". The flavor is the browser's own ECMAScript engine, nothing emulated and nothing sent over the network.

Where regex testing goes wrong

Three failure modes show up constantly. First, the reported positions are UTF-16 code-unit offsets, so a string containing an emoji counts as two units; a match that follows an emoji reports an index that looks one short if you are counting characters by eye. Second, an empty pattern or an empty test string does not run the engine at all — the tool prints a short prompt instead, which is its way of saying the inputs are incomplete. Third, and most serious, a pattern with nested quantifiers such as (a+)+ tested against a long run of a's can trigger catastrophic backtracking, where the engine explores an exponential number of paths; this page has no timeout, so such a pattern can freeze the tab. Keep quantifiers simple and test on realistic input before pointing the pattern at a production log file.

Regex questions that come up mid-debugging

Why does my pattern behave differently than it did in another language?

This tool runs the JavaScript engine, and every regex flavor has its own dialect. PHP, Python, and Ruby support constructs like \A and \z, conditional groups, or possessive quantifiers that JavaScript either lacks or spells differently. Named groups use the (?<name>...) form here, and \d matches ASCII digits only unless the u flag is added. A pattern written for one engine should always be re-verified in the engine it will actually run in.

Why are the results empty when the text obviously contains my string?

Start with the anchors. A caret and dollar sign without the m flag pin the match to the very start and end of the entire test string, so a pattern like ^apple$ will not match "I ate an apple". Next suspect case sensitivity, then check whether the g flag is really enabled if you expect several matches instead of one.

How do I see what each part of the match captured?

Put parentheses around the sections you care about. The engine numbers them from left to right, $1 being the first opening parenthesis, and the results panel prints each numbered group plus any named groups under the match entry. Optional groups that took part in no alternative display as undefined rather than quietly vanishing.

What does the position range in the match header mean?

It is the zero-based character range the match occupies in the test string: the first number is where the match starts, the second is one past the last character included. A match of "123" inside "abc123" would show 3–6, which reads oddly until you remember the end index is exclusive.

Can this tool freeze on a nasty pattern?

It can. The page runs the pattern in your browser with no timeout, and certain combinations of nested quantifiers explode the search space exponentially. If a test hangs, refresh the tab, simplify the pattern, and rebuild it in small pieces rather than debugging the whole expression at once.

When regex is the right tool

Use it to validate formats — phone numbers, identifiers, dates in a fixed shape — or to extract structured fields from logs and CSVs, and use this page to iterate on a pattern before committing it to code. Skip regex when the input is genuinely structured: HTML, JSON, and XML deserve real parsers, because regex approaches them as flat text and breaks on nesting. Skip it when a literal find-and-replace does the job, and skip it on untrusted adversarial input, where catastrophic backtracking turns into a denial-of-service vector.