ToolsTray

Star a tool to keep it here.

Regex Tester and Explainer

A regex tester that reads the pattern back to you in plain English as it highlights every match and group.

Result

Type a pattern to see matches.

Every match is marked in the test string as you type.

Any of d g i m s u v y

Show

Match count

0matches

Start here

  1. Put the expression in Pattern with the slashes left off, or paste the whole /…/gi literal and let it split itself up. Flags is the narrow box beside it and starts on g.
  2. Drop real text into Test string. Log lines, an API response, a column out of a CSV. Matches are marked as you type, and the count sits under the two panes.
  3. When something looks wrong, switch Show to Explains and read the tree before you start guessing at it. Switch to Replace if what you want is the rewritten text rather than the report.

About this tool

Type a pattern in the top box and every match is marked in the test string as you pause. The engine is the JavaScript RegExp your browser already ships, so this regex tester and the code you are writing the pattern for agree by construction. Flags start on g. Take the g out and you get a single match, exactly the way RegExp.exec behaves.

Paste a whole literal and it takes itself apart. Drop /(\d{4})-(\d{2})/gi into Pattern and the slashes come off, gi moves across into Flags, and a note under the panes says what moved. That works for d, g, i, m, s, u, v and y. Type the same thing by hand and it stays as typed, because /usr/bin/ is a perfectly good pattern. Anything that will not compile as a real expression stays exactly as you pasted it.

Matches lists every one beside the test string: the text it matched, the character index it started at, and a numbered chip for each capturing group. A group that took part but captured nothing reads (empty) rather than quietly vanishing, which is usually the detail you were hunting for. A zero-width match, the sign of a quantifier happy to match nothing at all, is labelled (empty match) and drawn as a thin caret in the text instead of showing you a blank.

Replace turns the same pattern into a rewrite. Substitution follows JavaScript to the letter: $1 and $2 for numbered groups, $& for the whole match, $<name> for a named one, and an empty Replace with deletes every match. The g flag decides whether every match is rewritten or only the first, and Copy takes the finished text.

Explains is the view I actually built this for. It turns the pattern into an indented tree, one row per piece, each row showing the raw token next to a sentence describing it. Capturing group #2. Any digit (0–9), repeated exactly 2 times. A word boundary. Not preceded by (negative lookbehind). Each flag you have set gets a line explaining its job, including d, which nobody remembers exists. When the pattern fails to compile, it reads as far as the break and says what stopped it. Reading somebody else's regex takes about ten seconds this way.

Good to know

  • JavaScript's dialect, and nothing else. Possessive quantifiers like a*+ and atomic groups like (?>…) are PCRE features the engine refuses outright. The anchors \A, \Z and \z have no JavaScript equivalent either. A pattern copied out of Python, PHP or Go wants checking rather than trusting.
  • The explainer describes shape, not meaning. It will tell you a group matches four digits. Whether those four digits are a plausible year is your problem. It also knows nothing about Unicode property escapes, so \p{L} gets written out as the literal text p{L} even though the matching itself handles it correctly once the u flag is on.
  • A pattern that backtracks catastrophically gets 1,500 ms inside a worker and then the worker is killed. The tab survives, a too-slow warning appears under the panes, and the last answer stays dimmed until the pattern is rewritten.

Frequently asked questions

Will this pattern behave the same way in Python or PHP?

Not necessarily. Matching goes through the JavaScript RegExp engine, which makes it exact for JavaScript and TypeScript and only approximate for anything else. Lookbehind, named groups and Unicode property escapes are shared with PCRE and Python. Possessive quantifiers, atomic groups and recursion have no JavaScript equivalent at all; the engine refuses to compile them.

Which flags can I set, and what does each letter do?

Eight of them, and every letter you type gets its own line in Explains with a description. g finds all the matches rather than the first. i ignores case. m makes ^ and $ hit line breaks. s lets a dot match a newline. u turns on Unicode mode and v is its stricter set-notation cousin. y pins matching to the current position. d records the start and end index of each match, which is the one people forget is there.

Nobody here knows what an old regex in our codebase does. Will it read it?

That is what Explains is for. Paste the pattern in, slashes and all, and it comes back as an outline: each group named and numbered, each character class spelled out, each quantifier turned into a phrase like repeated between 2 and 4 times. Lookaheads and lookbehinds get their own headings, and a backreference is described as the same text matched by group #1.

Why does my pattern find nothing when the text is right there?

Four causes account for most of it. A dot will not cross a newline until you add s. The ^ and $ anchors mean the start and end of the whole string until you add m. Case matters until you add i. And a character the engine treats as syntax, like a dot or a question mark or an opening bracket, needs a backslash in front of it to match itself. The tree shows which one you hit, because it prints how each piece is being read.

How do I turn 2026-08-12 into 12/08/2026?

Pattern (\d{4})-(\d{2})-(\d{2}), replacement $3/$2/$1. Numbered groups follow the order of the opening brackets, so $1 is the year here. Named groups work as well if the pattern uses them: (?<year>\d{4}) is referenced as $<year>. Leave g on to rewrite every date in the box rather than the first.

What does the too-slow message mean?

Your pattern is backtracking exponentially. It is nearly always a quantifier inside a quantifier over text that can be split more than one way, the classic being (a+)+$ run against a long line of a characters with something else on the end. Matching happens in a worker on a 1,500 ms budget so the page stays usable; when the budget runs out the worker is terminated and you get the warning in place of a hung tab.

Missing something in Regex Tester and Explainer? Suggest a feature →

Tell someone who needs this.