Skip to content

Google Tag Manager: Google Analytics 4 [GA4] Events Setup with a single Tag

David Vallejo

If you’re using Google Tag Manager for setting up Google Analytics 4 tracking you may have noticed the need of creating a separate tag for each event we want to track. While this gives a really nice and easy-to-understand photo of the current used events, it may add a ton of work for maintaining them, for example, if at some point we end up having hundreds of them.

In Universal Analytics many implementations were using a single event tag for tracking all their events, using a unique dataLayer event , and then mapping the event category, event action, event label and event value.

This cannot be done in Google Analytics 4, because we have an open specter of parameter / dimensions names which we have to individually map to each of the events we configure.

In this post, we’ll be learning today how to do all our events tracking using just a single GA4 Event Tag, while keeping all the features and functionality we’d have if we were setting up individual event tags.

Defining our dataLayer pushes

The first thing we need to do is to define how are we going to push the data to our dataLayer, and this is the way we’ll need to do it:

   'event': 'my_action_name',
   'event_params': {
        'param1': 'value1',
        'param2': 'value2'
   'user_properties': {
        'prop1': 'value1',
        'prop2': 'value2'

At this point you may wonder why we’re grouping our event parameters rather than just pushing them into push like:

   'event': 'my_action_name',
   'param1': 'value1',
   'param2': 'value2'
   'prop1': 'value1',
   'prop2': 'value2'

There’s a reason for this, Google Tag Manager dataLayer is cumulative, which means that all new pushed data keeps being added to the internal data model. If we push a param1 key it will not only be available for the current event, but it will be present on the subsequent ones. Since we want to use a single tag, this means that this data would be added to the next events.

In order to deal with this, we’ll be using a v1 dataLayer variable to read the event_params and user_properties. Because this way all the data contained within these keys will be overridden with all the pushes.

If you are wondering which are the differences between the v1 and v2 dataLayer variables:

v1Doesn’t support nested notation
Keys are overwritten rather than merged
v2Support for nested objects notation ( one.two.three )
Key are merged

Let’s see this with an example so we can understand this better. We have these 2 dataLayer pushes, each of them having a different event_parameter attached to them:

   'event': 'outgoing_click',
   'event_params': {
        'clicked_url': 'Simo Blog'
   'event': 'tab',
   'event_params': {
        'tab_id': '1'

Then in Google Tag Manager we’d be defining two variables. let’s name them {{dl - clicked_url}} and {{dl - tab_id}} . Let’s see how they would look depending on how we read them

{{dl - clicked_url}}{{dl - tab_id}}
First Pushv1: Simo Blog
v2: Simo Blog
v1: undefined
v2: undefined
Second Pushv1: undefined
v2: Simo Blog
v1: 1
v2: 1

If you check the table above if we were using the v2 version, the second event would end attaching the clicked_url value to the event, which is something that we don’t want.

It’s true that if you’re creating individual tags for each event, it’s gonna be hard this would be affecting you all (but it’s not impossible), in any case, let’s learn how to prevent this situation, and also let’s set up GTM to handle all of our GA4 events within a single tag.

Google Tag Manager Setup

The core Event Tag

First thing first, we need a new GA4 Event tag in our container. For now, the only thing we’ll configure here is the Event Name to be the in-build {{Event}} variable which will read the actual dataLayer push event value and pass it back to our GA4 tag event name.

For now, that’s all we’ll be doing, before mapping up the event parameters and user properties , we will learn how would need to set up the variables to relay on the v1 version.

The variables

This may be a bit confusing for some people, but as we explained before the need to use a version 1 parameter, but we also mentioned this dataLayer variable type does not support nested values. The workaround for this is using a variable that grabs the main key ( user_properties, event_params ) and then having some Custom JS variables returning the values we need.

Let’s start it, create these two variables and remember to set them as Version 1

The next step is creating a variable for each of the values we want to have access to, in our case, we’ll have 2 of them.

As you can see instead of reading them from the dataLayer, we are reading them from the v1 variable created. This will cause the event_params to be reset each time, and the values will be undefined for the parameters NOT being added to the current push. This functionally is like all the events/user data will be “reset” on each event push.

Mapping the variables

Now that we have our GA4 Event Tag and also our dataLayer variables, it’s time to map these last ones to the first one.

Variables Mapping

In your personal case, you’ll need to map as many parameters/variables as different values you’ve defined across all your events.

Setting up the triggers

Wait for a second, you said that we’ll be keeping all the features as doing the tracking with individual tags, with the setup we won’t be able to check which events are configured or we won’t be able to pause and specific event.

That’s where our trigger comes into scene, we’ll define a firing trigger that will trigger on .* the events, but only if the current event name is within an events lookup table.

Create a lookup table type variable in Google Tag Manager, on this table we’ll define an output of 1 for all the events name we want to enable ( ie: that will fire our GA4 event tag ), and then a default value of 0

As you may have already guessed this variable will be used as a condition in our trigger this way:


All this may look more convoluted than it really is, but it leads to some really interesting benefits.

  • We only have to set up one single tag
  • We still have the lookup table to control and review which events are defined and firing

Even if we end up going with the path of adding dozens or hundreds of event tags, the issue of having the dataLayer adding some parameters to some event that it should just because it was pushed before may be present on your setup. So I recommend using the combination of v1 and v2 variables to get rid of this problem.

At some point, if you have many events you could group them and use an event/trigger/lookup for each group 🙂