How to Track Analytics with Redux and Google Tag Manager

Written by Johanna Lee

How to Track Analytics with Redux and Google Tag Manager

Google Tag Manager (GTM) makes it quick and easy for digital marketers to manage what events are sent to analytics servers such as Google Analytics (GA). However, integrating GTM and developing a maintainable tagging strategy is challenging for those who are new to it. Here I am introducing an open source project called ReduxBeacon that resolves the challenge with GTM integration for apps using Redux or ngrx/store.

The aim of this tutorial is to walk you through the basic setup of the library in a sample application. By the end of the tutorial, you will be able to track GA pageviews and GA custom events on any Angular2 application that uses Redux.


Sample App Overview

angular2-redux-example is built with Angular 2, Redux, and Webpack and used at Rangle as a teaching material. It is a counter that can increment and decrement by button clicks. Also, it comprises a simple user authentication and a navigation to a secondary page using ng2-redux-router.

example-app

Prerequisites

src/index.html

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <!-- Google Tag Manager -->
            <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
            new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
            j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
            'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
            })(window,document,'script','dataLayer','GTM-XXXXXX');</script>
            <!-- End Google Tag Manager -->

            ...

        </head>
        <body>
            <!-- Google Tag Manager (noscript) -->
            <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXX"
            height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
            <!-- End Google Tag Manager (noscript) -->

            ...

        </body>
    </html>

The code snippet above shows where your GTM container snippet should be added (you will have to replace GTM-XXXXXX with your own GTM ID that you created before).

Google Tag Manager Setup

You need to create or own a container that is comprised of a set of macros, rules and tags for GTM. In this tutorial we will use the the starter container that Redux-Beacon provides for the quick and easy event tracking.

  1. Download GA starter from ReduxBeacon.
  2. Login to your own GTM container
  3. In the top navigation, click through to the ADMIN
  4. Under the CONTAINER options, click on the Import Container
  5. Choose GA starter as your container file

import-ga-starter

After importing the starter container to GTM, you should see some changes in the GTM workspace.

workspace-changes

Assign GA_TRACKING_ID with your own GA Account ID (find your GA Account ID).

Now we are ready to debug our container for the first time! You can find How to preview and debug containers here.
Having GTM's preview mode on, you can just run the following commands in angular2-redux-example folder.

npm install  
npm run dev  

You should be able to see the app running on localhost:8080 with GTM debugger.

first-gtm-debugging

ReduxBeacon Installation and Configuration

Install ReduxBeacon by running npm install redux-beacon --save.
Next, we import createMiddleware from ReduxBeacon and create a simple event map for LOGIN_USER action (the action is defined in src/actions/session.actions.ts). We define the map as eventDefinitionsMap in src/app/sample-app.ts.

...

import { createMiddleware } from 'redux-beacon';  
import { GoogleTagManager } from 'redux-beacon/targets/google-tag-manager';

const loginUserEventDefinition = {  
  eventFields: (action, prevState) => ({
    hitType: 'event',
  })
};

const eventsMap = {  
  'LOGIN_USER': loginUserEventDefinition,
};

const analyticsMiddleware = createMiddleware(eventsMap, GoogleTagManager);

...

export class RioSampleApp {

  ...

  constructor(
    private devTools: DevToolsExtension,
    private ngRedux: NgRedux<IAppState>,
    private ngReduxRouter: NgReduxRouter,
    private actions: SessionActions,
    private epics: SessionEpics) {

    middleware.push(createEpicMiddleware(this.epics.login));

    // create and push ReduxGTM
    middleware.push(analyticsMiddleware);

    ngRedux.configureStore(
      rootReducer,
      {},
      middleware,
      devTools.isEnabled() ?
        [ ...enhancers, devTools.enhancer() ] :
        enhancers);

    ngReduxRouter.initialize();
  }
}

If you run npm run dev with GTM preview mode on and log in with one of the credentials given in server/user.json (e.g. Username: "user", Password: "pass"), then you would see event event logged in GTM debugger.

login-user-event

Enhance eventDefinition

Now we have a very basic event that GTM debugger reports. However, this event does not match with any of our GTM tags. To find a proper tag for the event, we need to provide more information in its eventDefinition by assigning a function to eventFields. Any function assigned to this property will receive the state of the application (before the action), and the associated action object (action, prevState) to specify dataLayer variables and pass them to GTM as a tag trigger. With this function we can define any variables we would
like to emit with the event. The returned object is required to have hitType, eventAction and eventCategory properties and the optional eventLabel and eventValue properties.

{
  hitType: 'event',
  eventAction, // defaults to "unkown action"
  eventCategory, // defaults to "unknown category"
  eventLabel, // defaults to "unknown label"
  eventValue, // defaults to "unknown value"
}

src/app/sample-app.ts

...

// import ReduxBeacon APIs
import { createMiddleware } from 'redux-beacon';  
import { GoogleTagManager } from 'redux-beacon/targets/google-tag-manager';

const loginUserEventDefinition = {  
  eventFields: (action, prevState) => ({
    hitType: 'event',
    eventAction: 'Click Login Button',
    eventCategory: 'Login',
    eventLabel: 'Login Attempt',
    eventValue: action.payload.username,
  })
};

const eventsMap = {  
  'LOGIN_USER': loginUserEventDefinition,
};

...

GTM debugger logs that user’s login action and now emits an event that has enough information to trigger GAEvents tag.

login-event-info

We can create eventDefinitions for other actions defined in src/actions/session.actions.ts and src/actions/counter.actions.ts.

Create PageView Event

Time to create a pageview which triggers GAPageViews tag. Similar to creating a GAEvents trigger, we assign a function to eventFields. The function should return an object with the below properties.

{
  hitType: 'pageview',
  page, // defaults to 'unknown page',
}

ng2-redux-router dispatches UPDATE_LOCATION action on every route change for which we can create a pageview event definition, hence, we need to import UPDATE_LOCATION from ng2-redux-router.

src/app/sample-app.ts

import { NgReduxRouter, UPDATE_LOCATION } from 'ng2-redux-router';

...

// import ReduxBeacon APIs
import { createMiddleware } from 'redux-beacon';  
import { GoogleTagManager } from 'redux-beacon/targets/google-tag-manager';

const loginUserEventDefinition = {  
  eventFields: (action, prevState) => ({
    hitType: 'event',
    eventAction: 'Click Login Button',
    eventCategory: 'Login',
    eventLabel: 'Login Attempt',
    eventValue: action.payload.username,
  })
};

const pageViewEventDefinition = {  
  eventFields: (action) => ({
    hitType: 'pageview',
    page: action.payload,
  })
};

const eventsMap = {  
  'LOGIN_USER': loginUserEventDefinition,
  [UPDATE_LOCATION]: pageViewEventDefinition,
};
...

If we refresh our app running with GTM preview mode in a browser and click on About Us link on the top nav bar, we see GAPageViews tag triggered on route change.

pageview-props

Conclusion

ReduxBeacon mitigates the challenge of synchronizing Redux actions to GTM events in your app. It definitely saves you time on studying for the third party integration for your app. Instead, it ensures you have more time for getting priceless feedback on product initiatives and insights that will grow a user base.