Skip to content

Regex Tables

Regex Tables are Lookup Tables with superpowers. Instead of matching exact values, they match patterns. Instead of just returning a fixed output, they can extract and reuse portions of the matched value. For URL categorization, content classification, and ID extraction, Regex Tables are often the cleanest tool available.

If you find yourself writing a Custom JavaScript Variable with a series of if (path.match(/^\/products/)) statements, a Regex Table is almost certainly the right replacement.

How Regex Tables differ from Lookup Tables

Section titled “How Regex Tables differ from Lookup Tables”

A Lookup Table matches the input exactly. A Regex Table treats each row’s Input as a regular expression and tests it against the input variable’s value.

FeatureLookup TableRegex Table
MatchingExact match onlyRegex pattern
Capture groupsNoYes ($1, $2, …)
Case sensitivityCase-sensitive by defaultConfigurable
Full match onlyAlwaysConfigurable
Use whenInput space is enumerablePatterns needed

Both use first-match-wins evaluation.

  1. Go to Variables in GTM and click New under User-Defined Variables.
  2. Select RegEx Table as the variable type.
  3. Choose the Input Variable — typically {{Page Path}}, {{Page URL}}, or a Data Layer Variable.
  4. Add rows with Pattern (regex) and Output values.
  5. Configure the match options per row:
    • Ignore Case: Makes the match case-insensitive
    • Full Match: The regex must match the entire string, not just a substring
  6. Set a Default Value for when no pattern matches.
  7. Name it with convention: RGX - description.

When checked, the pattern /products/ matches both /Products/ and /PRODUCTS/. Use this when your URL structure or input values might vary in case.

This is the most important and most misunderstood option.

  • Full Match OFF (default): The regex must match somewhere in the input string. /products/ matches /products/jackets/leather-jacket.
  • Full Match ON: The regex must match the entire input string. /products/ only matches when the input is exactly /products/.

With Full Match OFF, you can use simpler patterns but risk over-matching. With Full Match ON, you need explicit anchors (^ and $) or your pattern needs to describe the entire value.

Recommendation: Leave Full Match OFF and use explicit ^ anchors to control where your pattern starts. This gives you predictable matching without needing to describe the entire URL.

Input Variable: {{Page Path}}

PatternOutputIgnore CaseFull Match
^/$homeOFFOFF
^/products/category/category_pageOFFOFF
^/products/[^/]+$product_detailOFFOFF
^/productsproductsOFFOFF
^/checkout/confirmationorder_confirmationOFFOFF
^/checkoutcheckoutOFFOFF
^/blog/[0-9]{4}/blog_postOFFOFF
^/blogblog_indexOFFOFF

Default Value: other

Order matters here. More specific patterns come before broader ones. /products/[^/]+$ (single segment after /products/) is more specific than /products (anything starting with /products), so it goes first. If you reversed them, every product detail page would match the broader pattern and return products instead of product_detail.

This is where Regex Tables shine beyond what Lookup Tables can do — capture groups let you extract portions of the matched string.

Input Variable: {{Page Path}}

PatternOutputNotes
^/products/([a-zA-Z0-9-]+)/([0-9]+)$$2Extracts numeric product ID from path like /products/leather-jacket/12345
^/order/([A-Z0-9]+)$$1Extracts order number from /order/ORD-56789
^/category/([^/]+)$1Extracts first path segment after /category/

$1 refers to the first capture group (first pair of parentheses), $2 to the second, and so on. The output value is replaced with the captured portion of the matched string.

Input: /products/leather-jacket/12345
Pattern: ^/products/([a-zA-Z0-9-]+)/([0-9]+)$
Output: $2
Result: 12345

Input Variable: {{Page URL}}

PatternOutput
`.(pdfdoc
`.(jpgjpeg
youtube\.com/watchvideo_youtube
vimeo\.com/[0-9]+video_vimeo
/download/download

Default Value: page

Input Variable: {{Click URL}}

PatternOutput
^https?://(www\.)?example\.cominternal
^https?://(www\.)?partner-site\.compartner
^https?://(www\.)?facebook\.comsocial_facebook
^https?://(www\.)?linkedin\.comsocial_linkedin
^mailto:email
^tel:phone
^https?://external

Input Variable: {{Page URL}} (or a URL variable extracting utm_source)

PatternOutput
googlegoogle
`(facebookinstagram
`(twittert.co)`
linkedinlinkedin
`(newsletteremail

Default Value: other

These are the patterns you will use in the majority of GTM Regex Table rows:

PatternMeaningExample
^Start of string^/products matches paths beginning with /products
$End of string/confirmation$ matches paths ending with /confirmation
.Any single characterorder.001 matches order-001, order_001
.*Zero or more of any character^/products/.* matches any sub-path under /products/
[abc]One of: a, b, or c[0-9] matches any digit
[^abc]Not one of: a, b, or c[^/]+ matches one or more non-slash characters
+One or more of previous[0-9]+ matches one or more digits
*Zero or more of previouscolou*r matches color and colour
?Zero or one of previouscolou?r matches color and colour
(abc)Capture group([0-9]+) captures the matched digits for use as $1
`(ab)`Either a or b
\dAny digitSame as [0-9]
\wWord characterSame as [a-zA-Z0-9_]
\.Literal dotexample\.com (unescaped . matches any character)

Never add a regex pattern to a Regex Table without testing it first.

In the browser console:

// Test your pattern against a sample input
var pattern = /^\/products\/([a-zA-Z0-9-]+)\/([0-9]+)$/;
var input = '/products/leather-jacket/12345';
var match = input.match(pattern);
console.log(match); // ['/products/leather-jacket/12345', 'leather-jacket', '12345']
console.log(match[2]); // '12345' — the $2 capture group

Using GTM Preview mode: After saving the variable, use Preview mode to check what value the variable returns on different pages. Look at the Variables tab in the Tag Assistant panel to see resolved values.

Regex evaluation is computationally more expensive than exact string matching. On a page that triggers dozens of GTM events, a Regex Table with 20 patterns evaluated on every event is not free. Keep your patterns efficient:

  • Anchored patterns evaluate faster: ^/products is faster than /products because anchoring stops the engine from trying to match at every position in the string.
  • Avoid greedy .* at the start: .*\/products has to backtrack through the entire string. Use ^[^?]*\/products or just \/products depending on what you need.
  • Fewer rows is better: If you can get from 20 rows to 10 by making patterns more general, do it.

For high-frequency events (every click on a busy page), consider whether the Regex Table variable is actually needed in the trigger condition or tag — or whether it could be computed once at page load by a Custom HTML tag and stored in a constant.

The most common bug with Regex Tables is having a broad pattern before a specific one:

// WRONG ORDER — /blog matches /blog/post too
Pattern 1: /blog → 'blog'
Pattern 2: /blog/([^/]+) → $1
// CORRECT ORDER
Pattern 1: /blog/([^/]+) → $1 (specific first)
Pattern 2: /blog → 'blog' (broad second)

Always put more specific patterns above broader ones.

example.com as a regex matches example-com, exampleXcom, etc. because . matches any character. Use example\.com to match a literal dot.

Forgetting capture groups return undefined on non-matches

Section titled “Forgetting capture groups return undefined on non-matches”

If you use $1 as an output and the pattern does not match, the row simply does not match — you fall through to the next row or the default. That is fine. But if a pattern matches but your capture group did not capture anything (because the group is inside an alternation that did not participate), you may get an unexpected result.

Test thoroughly.