Analytics

How to Track A/B Test Events in GA4 (Step by Step)

Track A/B test events in GA4: the impression event, custom dimensions, the Exploration report and the timing mistakes that silently corrupt the data.

Flat illustration of small geometric data markers flowing along a curved line from a browser window into a layered funnel, with a rising line chart beside it

GA4 does not know what an A/B test is. It has no experiment object, no variation dimension and no built in significance calculation, so tracking a test there is entirely a design job: you decide which event proves exposure, which parameters carry the experiment and variation identifiers, and which report puts them side by side. This guide is a child of the complete guide to GA4 and A/B testing and covers only the implementation path, from the first dataLayer push to a report you can trust enough to paste into a significance calculator.

The short version: send one custom event when a variation is rendered, carry two parameters on it, register those two parameters as event-scoped custom dimensions, wait for the dimensions to start populating, and build an Exploration that groups your conversion by the variation dimension. Everything else in this article is about the ways that sequence goes wrong.

Why tracking an A/B test in GA4 needs a structure of its own

Google Optimize was retired on 30 September 2023, and with it went the native integration that used to inject experiment and variant dimensions into Analytics for free. Since then, every team running tests alongside GA4 has been rebuilding the same plumbing by hand, usually badly, because the default instinct is to tag the conversion instead of the exposure.

Tagging the conversion is the original sin here. If you only mark conversions with the variation name, you have a numerator with no denominator: you know 410 people bought after seeing variation B, but you have no idea how many saw it at all, so you cannot compute a rate, and without a rate there is no test. The unit of an A/B test is the exposed visitor, so exposure is what has to be tracked first.

What you tag What you can compute Verdict
Only the conversion Absolute conversions per variation Useless: no denominator, so no rate and no test
Only page views by URL A rate, if the variations live on different URLs Only works for split URL tests, breaks for same page tests
The exposure, plus the conversion Rate, lift, confidence interval, p-value The only structure that supports a real reading

Send a single custom event, fired once per visitor per experiment, at the moment the variation becomes visible. Two parameters travel with it: the experiment identifier and the variation identifier.

// Fire this from the same code path that finishes applying the variation,
// not from a generic page load handler.
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
  event: 'experiment_impression',
  experiment_name: 'hero_cta_test',
  variation_name: 'B'
});

Three properties of this design matter more than the naming:

Path of an A/B test exposure from the browser to the reportThe testing snippet assigns a variation, applies it to the page, and only then pushes an impression event with the experiment name and the variation name to the data layer. A tag manager trigger listens for that event, reads the two parameters as variables, and sends them in a GA4 event tag. Analytics stores them as event scoped custom dimensions, which an Exploration report then uses to group conversions by variation.assignvariationapply it tothe pagepush impressioneventtrigger andtag firestored asdimensionsThe order is the whole point: an impression that is pushed before the variation is applied counts peoplewho never saw it, and that inflated denominator lowers the measured rate of one side only.The conversion event stays exactly as it already is. It is joined to the variation by the same user, not by a parameter.
The impression event is the denominator of the experiment. Everything downstream inherits whatever error you introduce here.

Registering the two custom dimensions

Parameters that are not registered as custom dimensions are collected but not reportable, which is the single most common reason a correctly implemented event produces an empty report. In the GA4 interface the path is Admin, then Custom definitions, then Create custom dimension, twice:

Dimension name Scope Event parameter What it answers
Experiment name Event experiment_name Which test this exposure belongs to
Variation name Event variation_name Which side of the test the visitor saw

Two operational details that cost teams a day each when they are missed. First, custom dimensions are not retroactive: they populate only for events collected after registration, so data from before that moment stays invisible in reports forever. Register the dimensions before you start the test, not after the first curious look at the numbers. Second, Google documents a limit of 50 event-scoped custom dimensions on standard properties, which is why the experiment identifier belongs in a parameter value rather than in a dimension per experiment.

Building the Exploration report

The default reports in GA4 will not show custom dimensions in a useful shape, so the reading happens in Explore, Free form:

  1. Import both custom dimensions and the metrics you need: total users, plus the conversion event count or key event count.
  2. Put Variation name in rows.
  3. Add a filter where Experiment name exactly matches the identifier of the running test, so concurrent experiments do not contaminate each other.
  4. Set the date range to the exact window the test ran, with no partial days at either end.
  5. Add the conversion as a column, so each row shows exposed users and conversions side by side.
Shape of the Exploration report that produces a readable A/B test resultA free form Exploration with variation name in rows, filtered to a single experiment name, showing exposed users and conversions for control and variation. Control shows nine thousand exposed users and three hundred and sixty conversions. Variation B shows nine thousand exposed users and four hundred and ten conversions. A note underneath warns that the report itself does not calculate significance.Rows: variation nameFilter: experiment name matches one testvariationexposed usersconversionsrateA (control)9,0003604.00%B9,0004104.56%This table is the input, not the answer. Nothing in Analytics turns these four numbers into a verdict.
The report you are aiming for has four numbers in it. What you do with those four numbers happens somewhere else.

Sending the event: tag manager or gtag directly

There are two ways to get that push into Analytics, and the choice has consequences beyond convenience.

With Google Tag Manager, the dataLayer.push above is picked up by a custom event trigger, two data layer variables read experiment_name and variation_name, and a GA4 event tag sends them. The advantage is that marketing can change the tag without a deploy, and the Preview mode lets you watch the event fire in real time before publishing the container. The cost is one more system that can be misconfigured, and a container publish step that people forget. The full tag manager setup guide walks through the trigger, the variables and the Preview check.

With gtag directly, you skip the container and call the Analytics library from the same function that applied the variation. The advantage is that the event cannot fire before the render, because it is the same line of code; there is no timing gap to introduce. The cost is that every change is a deploy, and the parameter names live in application code where marketing cannot see them.

Criterion Tag manager gtag directly
Changing parameters without a deploy Yes No
Risk of firing before the variation renders Higher, the trigger listens to an event you must place correctly Lower, the call sits inside the render path
Debuggability before going live Strong, Preview mode replays every fire Weaker, usually the network tab and the DebugView
Ownership Marketing or analytics Engineering

Neither is wrong. What is wrong is mixing them, sending the same impression from both paths and doubling the denominator on one side of the test.

The sampling limit on high-volume properties

Explorations are not always exact. Google documents that standard properties apply sampling to Exploration reports when the query covers more than roughly 10 million events for the selected date range, and marks the report with a sampling indicator. A sampled figure is an estimate with an error band of its own, and feeding an estimate into a significance calculation compounds two uncertainties into a number that looks precise and is not.

Three ways out, in order of preference: shorten the date range until the indicator disappears; narrow the segment so the query touches fewer events; or use the BigQuery export link and count the raw events with SQL, which removes sampling entirely. The BigQuery daily export on the free tier is documented at a limit of 1 million events per day, so very large properties need the paid path.

Why the GA4 numbers will not match your testing tool

They will differ, and expecting them to match to the visitor is the fastest way to lose a week chasing a bug that is not there. Four structural reasons, none of which is a defect:

The practical rule: use GA4 to understand what happened around the experiment, segment by device, channel and geography, and inspect the downstream funnel. Read the verdict where the assignment lives. If the two disagree in direction rather than in magnitude, that is worth investigating; a gap of a few percent in absolute counts is not.

Timing and counting mistakes that corrupt the reading

A worked example, with the numbers exported from the report

Suppose you built the Exploration exactly as described and exported these rows, grouped by the variation dimension: control (A) had 9,000 users with the impression event and 360 conversions; variation B had 9,000 users and 410 conversions.

With a p-value of 0.066, above the 0.05 threshold, and a 95% confidence interval on the difference running from roughly minus 0.04 to plus 1.15 percentage points, crossing zero, the result is not statistically significant, despite a raw relative lift of nearly 14%. This is exactly the scenario where declaring a winner early, because the GA4 table “looks good”, becomes the most common mistake on the list above. The honest reading is: keep collecting, or accept that at this sample size the observed difference is still compatible with noise. Check the same arithmetic in the calculator below by pasting the numbers exported from your own report:

Statistical significance calculator
Control (A)
Variation (B)
Control (A) · Rate-
Variation (B) · Rate-
Relative lift-
p-value-
95% CI of the difference-

Two-sided two-proportion z-test. "Not significant" almost always means not enough sample, not that the versions are equal.

Make this automatic with Donnu

The work this guide covered is real engineering: designing the exposure event, guarding it so it fires once and at the right moment, registering dimensions before the test starts, building the Exploration and then not fooling yourself with sampling or peeking. GA4 does none of it for you, and a mistake in any one step can quietly poison weeks of data.

Donnu handles the other half of the problem. The stable 50/50 split and the honest significance calculation happen inside the tool itself, at the moment of assignment, without a hand built impression event or an Exploration report standing between you and the verdict. GA4 stays useful as a complementary source of behaviour and revenue, but the decision about the test no longer depends on it. Start a free trial and let the split and the statistics be somebody else’s job.


Read also: GA4 and A/B Testing: The Complete Integration Guide · A/B Testing Statistical Significance · Common A/B Testing Mistakes and Validity Threats · Leia em português

References

Frequently asked questions

Does GA4 have a native A/B testing report?
No. GA4 has no experiment object, no variation dimension and no significance calculation of its own. Since Google Optimize was retired in September 2023, the native integration that used to populate those reports no longer exists. Everything you see about an A/B test inside GA4 today is something you built: a custom event that says which variation the user saw, two custom dimensions registered from that event, and an Exploration report that groups conversions by one of them. GA4 stores and slices the data; it does not decide the winner.
Should I send the variation as an event parameter or as a user property?
Send it as an event parameter on the impression event, and register it as an event-scoped custom dimension. A user property is overwritten by the last value it receives, so a visitor who is exposed to two different experiments, or who is reassigned after clearing storage, ends up labelled with only the most recent value, which quietly reassigns their earlier conversions to the wrong variation. Event scope keeps each event attached to the variation that was actually on screen when it fired.
When exactly should the impression event fire?
After the variation has been applied to the page, never before. If the event fires on page load and the variation is applied a few hundred milliseconds later, every visitor who leaves during that window is counted as exposed to a variation they never saw. The bias is not symmetrical either, because the variation branch usually carries the extra work, so it accumulates more of those phantom exposures and its denominator inflates. Fire the event from the same code path that finishes rendering the variation.
Why do my GA4 numbers not match the numbers in my testing tool?
They almost never match exactly, and small gaps are expected rather than alarming. The four usual causes are consent mode, which suppresses part of the traffic in GA4 but not in a tool that runs before the banner; ad blockers, which block the GA4 endpoint at a higher rate than a first-party snippet; different counting units, since GA4 counts users and sessions while the testing tool counts assigned visitors; and different attribution windows for the conversion. Use GA4 for behaviour and segmentation, and read the verdict where the assignment itself lives.
Does GA4 sampling affect my A/B test reading?
It can, in Explorations on high-volume properties. Google documents that standard properties apply sampling to Exploration reports above roughly 10 million events for the selected date range, and shows a sampling indicator at the top of the report. A sampled number is an estimate with its own error, which is not a safe input for a significance calculation. If the indicator appears, shorten the range, narrow the segment, or export the raw events through the BigQuery link and count them yourself.
How many custom dimensions do I need for this?
Two are enough, and two is what you should use: one for the experiment identifier and one for the variation identifier. Keeping them separate lets you run several experiments at once and still filter one at a time. It also matters because standard GA4 properties are limited to 50 event-scoped custom dimensions, so a scheme that creates a new dimension per experiment exhausts the quota quickly and forces you into cleanup work that breaks historical reports.