DEV Community

Count Of Tuscany
Count Of Tuscany

Posted on

Your Regex Works in JS but Fails in GA4? Full-Match, RE2, and the Patterns That Actually Work

You build a regex, test it in your editor, it works. You paste it into a GA4 segment or comparison and... zero rows. No error, no warning, just silence.

Two features of GA4 explain almost every one of these failures.

1. GA4 matches the whole value, not a substring

In JavaScript, /blog/ matches "/blog/my-post" happily. In GA4, filters use full-match semantics: /blog/ matches the value /blog and nothing else.

That's why every GA4 pattern you find in the wild is wrapped in .*:

# matches any page path containing /blog/
.*/blog/.*
Enter fullscreen mode Exit fullscreen mode

Two flavors to remember:

  • "is one of my regex patterns" (segment dimensions): full match, wrap with .*
  • "partially matches regex" (some comparison surfaces): substring match, no wrapping needed

Test the exact flavor you'll use - mixing them up is the number one source of "my regex works in the preview but not in the report".

2. GA4 runs RE2, not JavaScript's RegExp

GA4 (like BigQuery, Go, and Rust) uses Google's RE2 engine. RE2 is linear-time - it can never blow up on adversarial input - but that guarantee costs it three PCRE families:

  • No lookahead / lookbehind: foo(?=bar), (?<=/)(\w+) - rejected
  • No backreferences: (foo|bar)\1 - rejected
  • No atomic groups / possessive quantifiers / recursion - rejected

The RE2-friendly mindset:

Instead of Write
foo(?=bar) (foo)bar - capture the part you need
negative lookahead (?! a negated class [^x], or an exclude filter
`(foo bar)\1`
non-greedy tricks plain .* - GA4 matches the full value anyway

Three patterns that actually work in GA4

Page path filter - all blog posts:

.*/blog/.*
Enter fullscreen mode Exit fullscreen mode

Event name filter - the ecommerce funnel:

(view_item|add_to_cart|begin_checkout|add_payment_info|purchase)
Enter fullscreen mode Exit fullscreen mode

Custom channel grouping - organic social:

.*(facebook|instagram|linkedin|twitter|x\.com|t\.co|youtube).*
Enter fullscreen mode Exit fullscreen mode

(Notice the escaped dot in x\.com - unescaped, it would also match xXcom.)

The workflow that stops the guessing

  1. Write the pattern in a tester that lets you paste your real values (the actual page_path rows, the actual event names - not your memory of them).
  2. Check which engine it targets. JavaScript-valid is not RE2-valid; if your pattern contains (?=, (?! or \1, it will never run in GA4.
  3. Only then paste it into GA4.

I kept failing at step 2 myself, so I built RegexLab - a free, browser-only tester that shows match/no-match per line and keeps the RE2 gotchas visible. There's also a pattern library with tested, copy-paste patterns (dates, URLs, UTM parameters, log levels), each with an RE2-compatibility note, and a dedicated RE2 vs PCRE page listing every unsupported construct with its replacement.

Nothing leaves your browser - the whole thing is client-side JavaScript RegExp under the hood.

TL;DR

  • GA4 = full match. Wrap substring patterns in .*
  • GA4 = RE2. No lookahead, no backreferences, no lookbehind
  • Test against your real data, with the right engine in mind, before you ship the pattern

What's the regex problem that cost you the most debugging time? Mine was a lookahead that silently matched everything in preview and nothing in production.

Top comments (0)