Analytics

Google Analytics 4

Report experiment exposures to Google Analytics 4 from your application, so you can compare the variations next to the events you already track there.

Introduction

To compare an experiment's variations in Google Analytics, your application tells Google Analytics which variation each user saw. You add a handler once, and you can then compare those users across the events you already send it.

Your application sends these events straight to Google Analytics, and ConfigDirector never holds your API secret.

What Google Analytics reads

Google Analytics identifies a variation through an experience_impression event with one parameter:

ParameterWhat you sendExample
exp_variant_stringCD-, the config key, a hyphen and the variation's nameCD-checkout-cta-copy-treatment-a

Google recommends that format, a tool ID, an experience ID and a variant ID joined by hyphens, so the value stays apart from other experiments and other tools. A value must be 100 characters or fewer on a standard property and 500 or fewer on a Google Analytics 360 property.

Add the handler

Add a handler for the configEvaluated hook where you create the client, in the browser or in Node.js. The browser is the simpler of the two, because the Google tag on your site already knows the visitor:

import { createClient } from "@configdirector/client-sdk";

export const client = createClient("YOUR-CLIENT-SDK-KEY", {
  hooks: {
    configEvaluated: ({ evaluation }) => {
      const variationName = evaluation.variation?.name;
      if (!variationName) return;

      gtag("event", "experience_impression", {
        exp_variant_string: `CD-${evaluation.key}-${variationName}`,
      });
    },
  },
});

await client.initialize({ id: "user-42" });

Only an experiment's evaluations carry a variation, so the check skips every other config, along with the in-code default value. It also skips a variation without a name, so name each one. In TypeScript, the gtag global needs the @types/gtag.js package.

From Node.js, the handler sends the event through the Measurement Protocol, which needs an API secret from your data stream and a client_id that matches the one the Google tag generated in the visitor's browser. A server cannot work that out from the context's id, so your application has to pass it along. The example passes it as a trait, and traits show in the ConfigDirector dashboard. A context without that trait is skipped.

The handler needs version 1.11.0 or later of the JavaScript SDK, or 1.13.0 or later of the Node.js SDK. React, Vue and React Native take the same handler in their hooks. The Experiments guide lists the hook for every other SDK.

With OpenFeature

In an OpenFeature application, a hook does the same job, with the variant as the variation's name. Every config has a variant, not only experiments: on any other config it is ConfigDirector's ID for the value. So attach the hook to the reads of your experiments instead of registering it for every read:

import { OpenFeature, type Hook } from "@openfeature/web-sdk";

const reportExposure: Hook = {
  after: (hookContext, details) => {
    if (!details.variant) return;

    gtag("event", "experience_impression", {
      exp_variant_string: `CD-${hookContext.flagKey}-${details.variant}`,
    });
  },
};

const client = OpenFeature.getClient();

const buttonLabel = client.getStringValue("checkout-cta-copy", "Start free trial", {
  hooks: [reportExposure],
});

A variation without a name also has ConfigDirector's ID for the value as its variant, so name each one. The hook needs version 1.11.0 or later of the Web provider, or 1.13.0 or later of the Node.js provider.

When the handler runs

The handler runs once per evaluation, whenever your application reads the config. So read an experiment where the user reaches the change rather than at startup, or users who never see it count as exposed.

A config read on every render sends an event each time, and Google Analytics keeps each one as its own event. So compare variations by users rather than by event count: a user counts once in an audience, however many of these events they sent.

Compare the variations

The outcomes you compare are the events your application already sends to Google Analytics, so there is nothing more to add to your code.

  1. Right after you start the run, create an audience for each variation: include users when an experience_impression event has an exp_variant_string that exactly matches, such as CD-checkout-cta-copy-treatment-a. Set its membership duration to cover the run.
  2. Compare those audiences in your reports, over the dates of the run, which the Experiment tab lists. A report shows up to four comparisons at once.
  3. When the run ends, archive the audiences to free up space for your next ones.

The handler also runs outside the split: before the run starts, after you ship and when a conditional rule serves a variation. An audience collects users from the moment you create it, and that keeps out the users served the default targeting rule value before the run. It can take 24 to 48 hours to fill. A conditional rule above the split can still make the numbers higher than the Exposures card, which counts only the contexts the split served.

To explore further, register exp_variant_string as an event-scoped custom dimension and use it in an Exploration. Google notes this is less accurate than audiences, because it also shows a user's events from before and after the experience.

Keep each variation's name for the length of the run, because a new name makes a new exp_variant_string value, which your audiences would not match.

Chart markers from an analytics integration are not offered for this tool; they are for Amplitude, Mixpanel, Datadog, Grafana and PostHog.