To track a custom query parameter or an A/B test variant in Google Analytics 4, you send it as an event parameter through Google Tag Manager, then register that parameter as a GA4 custom dimension in the admin. GA4 recognizes the five UTM parameters automatically, but it treats anything you invented, a variant, a promo code, an audience tag, as invisible until you register it. Registration is a two-minute job, and it collects data only from the moment you save it forward. There is no backfill, which is why the time to set this up is before the test runs, not after.
This is the companion to our guide on adding UTM and custom parameters to your ad URLs. That post gets the data onto the page and into your form entries. This one gets it into GA4’s reports, so you can compare variants, campaigns, and segments inside the analytics itself.
UTM parameters versus custom parameters in GA4
The distinction is the whole reason this post exists, so it is worth being precise.
GA4 ships knowing what utm_source, utm_medium, utm_campaign, utm_content, and utm_term mean. It reads them off the landing page URL automatically and files them into the Session source, Session medium, and Session campaign dimensions with zero configuration. That is why the sibling post never mentions GTM: for standard UTMs, none is needed.
A custom parameter is any key you made up. GA4 has no built-in slot for variant, so by default the value rides along in the page URL and goes nowhere useful. You can glimpse it, but you cannot cleanly report on it, until you do two things: capture it as an event parameter, and register that parameter as a custom dimension.
The quick look, if you only need a glance
Before building anything, know that GA4 already carries the full URL in a dimension called Page path and query string. In an Exploration report you can add that dimension and see your ?variant=hero_a right there in the path. It works for a one-time spot check.
It is a poor fit for ongoing reporting, for two reasons. It shows the entire query string rather than isolating your one key, so hero_a is buried in a soup of UTMs and click IDs. And GA4 restricts which metrics that dimension can pair with, so you will hit “incompatible” walls the moment you try to cross it with anything interesting. For a real test you want a clean dimension. That means the setup below.
How GA4 custom dimensions actually work
Three facts save most of the confusion:
An event parameter and a custom dimension are two halves of one thing. The event parameter is the data arriving on an event (your variant value, sent with a page view). The custom dimension is the registration in GA4’s admin that makes that parameter show up in reports. Send the parameter without registering it and the data exists in DebugView and in BigQuery but is invisible in every standard report and Exploration. This is the single most common GA4 tracking mistake.
There is no backfill. A custom dimension collects data from the moment you register it, going forward. It will never show you values from before it existed. If you launch a test on Monday and register the dimension on Friday, you have lost four days. Register first.
You have a limited number of slots. Standard GA4 properties allow 50 event-scoped custom dimensions and 25 user-scoped. That is plenty for most sites, but it is not infinite, and deleting a dimension only frees its slot after a 48-hour archive delay. Do not register a throwaway dimension for every ad-hoc test; reuse a general one like variant across tests instead.
Event-scoped or user-scoped?
For A/B tests and campaign parameters, you almost always want event-scoped. The variant a visitor saw is a property of that visit, not a permanent trait of the person. User-scoped is for durable attributes like membership tier or account type. Choosing the wrong scope is the second most common mistake, and it produces reports that look plausible and are quietly wrong, so decide deliberately.
The setup, step by step
This assumes you already run GA4 through Google Tag Manager, which is the standard setup. We will use an A/B variant as the worked example; a coupon code or audience tag follows the identical path with a different key.
1. Capture the parameter in GTM
Create a variable that reads the value off the URL:
- In GTM, go to Variables, then New, and choose the URL variable type.
- Set Component Type to Query.
- In the Query Key field, enter your parameter name exactly:
variant. - Name the variable something clear, like
url - variant, and save.
GTM now reads ?variant=hero_a off any page URL and hands you the value hero_a.
2. Send it to GA4 as an event parameter
Add the value to the event that should carry it. For a landing page test, that is usually the page view:
- Open your GA4 Configuration or GA4 Event tag (whichever fires your page views).
- Under event parameters, add a row.
- Parameter Name:
variant(snake_case, and keep it identical everywhere). - Value: select the
url - variantvariable you just made, using the{{ }}variable picker. - Save, then Preview the container and click a tagged URL. In GA4’s DebugView, confirm the page_view event now carries
variant = hero_a.
Publish the GTM container once DebugView confirms the value is arriving.
3. Register the custom dimension in GA4
This is the step that makes it visible, and the step people forget:
- In GA4, go to Admin, then Data display, then Custom definitions.
- Click Create custom dimension.
- Dimension name: a human-readable label like
Variant(this is what shows in reports). - Scope: Event.
- Event parameter:
variant, matching the GTM parameter name exactly. A case mismatch (Variantversusvariant) silently breaks the whole thing. - Save.
Data appears within roughly 24 to 48 hours, and only for traffic from this point forward. Register it before you launch the test.
4. Report on it in an Exploration
Once data is flowing:
- Go to Explore and start a blank Free form exploration.
- Import your
Variantdimension and a metric that matters: Conversions, or a specific key-event count. - Put
Variantin Rows and the metric in Values.
You now have a clean side-by-side: hero_a versus hero_b, by conversions. That is the readout an A/B test is supposed to produce, and it lives in GA4 next to all your other data rather than trapped in a separate tool.
Where the custom parameters come from in the first place
This post assumes the parameter is already on your URLs. If it is not yet, that is the job of the sibling guide: adding custom query parameters to your ad URLs across Google, Meta, and TikTok, and capturing them into Gravity Forms and Ninja Forms hidden fields at the same time.
The two paths are independent and worth keeping straight:
- Onto the lead record runs through the query string, a first-party cookie, and your form’s hidden fields. No GTM, no GA4. That is the sibling post.
- Into GA4 reports runs through GTM and the custom dimension. That is this post.
Most teams need both, because they answer different questions. The lead record tells your sales team which variant a specific booked call came from. GA4 tells you which variant won across all traffic. Set them up together and a single tagged URL feeds both.
Using this for real A/B tests
A few practical notes once the plumbing works, because the mechanics are the easy part and the test design is where people go wrong.
When the two variants are different URLs, this approach is the whole test. Point one ad at /offer/?variant=hero_a and another at /offer-b/?variant=hero_b, register the dimension, and GA4 splits the results. You are running the test with your ad platform as the traffic splitter.
When the two variants are the same URL and a testing tool swaps the content in place, that tool tracks its own split, so the custom dimension is redundant for the win/lose decision. It is still useful if you want the variant recorded on the lead as well as in the tool, which is the case most teams overlook.
Watch your sample size before you call a winner. A custom dimension makes it trivial to see a difference between two variants. It does nothing to tell you whether that difference is real or noise. A handful of conversions per variant is not a result. This is where conversion rate optimization stops being “look at the bigger number” and starts being a discipline: enough traffic, enough time, and a difference large enough to act on.
Do not pass personal data as a dimension value. This one is not a style note, it is a Terms of Service line. Passing names, email addresses, or phone numbers into a GA4 custom dimension violates Google’s terms and can get your property permanently deleted. Keep parameter values to non-personal labels: hero_a, summer15, retargeting_30d. If a URL parameter might carry personal data, strip or exclude it before it reaches GA4.
A note on consent
Reading a parameter off the URL and attaching it to analytics events is analytics processing, and in jurisdictions that require consent for it, it should respect your consent settings the same way your GA4 tags do. If you run a consent banner with GTM’s consent mode, your custom-dimension tags inherit that gating along with everything else. The same first-party attribution caveat from the sibling post applies here.
Frequently Asked Questions
Why is my GA4 custom dimension showing no data?
The most common cause is a name mismatch between the event parameter sent from Google Tag Manager and the parameter registered in GA4. They must match exactly, including case, so variant and Variant are two different things and only one will collect data. The second most common cause is that you are looking for historical data: custom dimensions have no backfill and only collect values from the moment you register them forward. Confirm the parameter is arriving in DebugView first, then confirm the registered name matches character for character.
What is the difference between an event parameter and a custom dimension in GA4?
An event parameter is the data that arrives on an event, for example a variant value sent with a page view through Google Tag Manager. A custom dimension is the registration in GA4’s admin that makes that parameter visible in reports and Explorations. You need both: sending the parameter without registering the dimension means the data is collected but invisible in standard reports, and registering a dimension for a parameter you never send leaves it empty.
Do I need custom dimensions for standard UTM parameters?
No. GA4 recognizes the five standard UTM parameters automatically and files them into the Session source, medium, and campaign dimensions with no setup. Custom dimensions are only needed for parameters GA4 does not know about, meaning any key you invented yourself, such as an A/B variant, a coupon code, or an audience segment.
How many custom dimensions can I create in GA4?
Standard GA4 properties allow 50 event-scoped custom dimensions and 25 user-scoped, plus 10 item-scoped for e-commerce. GA4 360 raises those limits. Deleting a dimension does not free its slot immediately; the slot becomes available again after a 48-hour archive period. Because slots are limited, reuse a general dimension like variant across multiple tests rather than registering a new one for every experiment.
Should the variant dimension be event-scoped or user-scoped?
Event-scoped, for A/B tests and campaign parameters. The variant a visitor saw is a property of that particular visit, not a permanent trait of the person, so it belongs on the event. User-scoped dimensions are for durable attributes like membership tier or account type. Choosing user scope for a variant produces reports that look plausible but attribute the value incorrectly across a user’s later sessions.
Can I track A/B tests in GA4 without a testing tool?
Yes, when your variants are different URLs. Tag each variant’s URL with a custom parameter like variant=hero_a, register it as a custom dimension, and point separate ads at each URL so your ad platform splits the traffic. GA4 then reports conversions by variant. A dedicated testing tool is worth it when both variants live at the same URL and the content is swapped in place, or when you want statistical significance calculated for you, which GA4 does not do on its own.
Get the whole loop working
Custom-dimension reporting is one half of a system, and it is the half that tells you which variant won across all your traffic. The other half puts the same data on the individual lead, so your sales team knows which version produced a specific booked call. To wire up that side, and to add custom parameters to your Google, Meta, and TikTok ads in the first place, see the companion guide on how to get UTM and custom parameters onto the lead record.
Measurement like this is infrastructure, not a report you buy at the end. That is why we configure it before anything launches on every marketing engagement, and why the same team that runs your paid advertising also builds the tracking that proves it worked.
Ready for marketing you can measure?
Tell us about your goals and we’ll show you exactly how we’d approach them: which channels, what it costs, and how we’ll prove it’s working. No obligation, no hard sell. If we’re not the right fit, we’ll tell you that too.
or call 919-200-0201