Home > References > SEO

React js google tag manager: React Google analytics problem solved

Last updated : October 8, 2022

Google tag manager, out of the box, is unable to track React js page navigations using react-router. The reason is how the react-router uses react virtual dom to manipulate the view. Unlike traditional anchor tags, React's router Link doesn't do a full page reload. Instead, the router Link only updates the altered dom elements and leaves the rest of the dom untouched. That is the expected behavior and much more efficient than manipulating the entire DOM.

This article explains three ways to integrate Google Tag Manager with React Js.

  1. Using Google-provided tag manager scripts with history change trigger.
  2. Using the npm package react-gtm-module with history change trigger.
  3. Directly triggering a modified Google Tag Manager script.

1. Integrate GTM with Google-provided tag manager scripts

In this method, I place the GTM scripts in the index.html file located in the public folder. Use the

Here is my index.html after the changes.

<!DOCTYPE html>
<html lang="en">
	<!-- Google Tag Manager - as high in the <head> of the page -->
	  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
	<!-- End Google Tag Manager -->
    <title>React App</title>
    <!--immediately after the opening <body> tag-->
	  <noscript><frame src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
	  height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
	<!-- End Google Tag Manager (noscript) -->
    <div id="root"></div>

The next step is to create a history change trigger. Proceed to this step to create a history change trigger in the GTM console. Now my app is all set to track page views.

2. Integrate GTM with the npm package react-gtm-module

There is an npm package to integrate GTM with React Js. It is called react-gtm-module.

I can install the package by running
npm install react-gtm-module --save
by editing the package.json file and adding "react-gtm-module": "^2.0.11" as a dependency.

Now I must place the tracking code in the application. The same rule applies to the tag manager scripts, but I cannot select index.html this time. I use App.js, but your choices may vary depending on your app architecture.

import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import TagManager from 'react-gtm-module';
import Users from './Users'
import Posts from './Posts'
import Login from './Login';
const App = () => {
  useEffect(() => {
	TagManager.initialize({ gtmId: 'GTM-XXXXX' });
  }, []);
  return (
          <Route path="/" component={Users} exact />
          <Route path="/posts" component={Posts} exact />
          <Route path="/login" component={Login} exact />
export default App;

The next step is to create a history change trigger. Proceed to this step to create a history change trigger in the GTM console. Now my app is all set to track page views.

3. Integrate GTM by directly triggering a modified Google Tag Manager script

I modified the GTM script so I could call it directly. Then I created a separate file to place my modified analytics script. My modified script is in Typescript, which is helpful for most users.

export const analytics = ((w: Window, d: Document, s: string, l: string, i: string) => {
    (w as any).dataLayer = (window as any).dataLayer || [];
    (w as any).dataLayer.push({'gtm.start':new Date().getTime(),event:'gtm.js'});
    var dl = l != 'dataLayer' ? '&l='+l : '';
    var scr = 'https://www.googletagmanager.com/gtm.js?id='+i+dl;
        To avoid Multiple installations of google tag manager detected warning
            var f = d.getElementsByTagName(s)[0],
                j: HTMLScriptElement = d.createElement("script")  
                j.async = true;
                j.src = scr;
    const scriptExists = (url: string) => {
        var scripts = document.getElementsByTagName('script');
        for (var i = scripts.length; i--;) {
          if (scripts[i].src == url) return true;
        return false;

Now I must call this function whenever the user clicks a link. Therefore, I need a common page shared by all web application pages.

useEffect(() => {
    analytics(window, document, 'script', 'dataLayer', 'GTM-AA12345');

That's it. I don't need to implement a history change trigger with this method.

Creating a history change trigger in the Google tag manager console

Now it's time to create a history change trigger in the Google tag manager console. Log into google tag manager and follow the below steps.

  1. Select workspace and click Triggers
    Google tag manager triggers
    Adding a trigger on Google tag manager
  2. To create a new trigger, click New -> Trigger Configuration and select History Change trigger from the list.
    History Change trigger
    Trigger Configuration Select History Change trigger
  3. Make sure to check All History Changes.
    Saving History Change trigger
    Give a name and save the trigger
  4. Name the trigger and click save.
    Saving History Change trigger
    Give a name and save the trigger

Don't forget to publish your workspace container. Now you would see a history change trigger for navigations within your application.

Debug google tag manager with tag assistant

We can use Google tag assistant to ensure all the intended triggers are triggered on your page navigations. To start a debug session, click the Preview button on the top right. Once you click start, the tag manager will open a debug window connected to your website. If it fails to connect the debugger to your website, close the tab and restart the debug session. I have experienced this issue several times. Once the session is successful, use the window to navigate your website links. You will notice the history trigger fires on each navigation.

Google tag manager debugger
Srart a debug session by clicking the Preview button on the top right
Google tag manager history change
Google tracks every transition by creating a history event

Verifying your tags with Google Tag Assistant

The easiest way to ensure your website tags are triggered is to install the Google tag assistant chrome extension. Tag assistant will also tell you if you have any duplicate tag issues. Install it from https://get.google.com/tagassistant.

Why does Google Analytics not track internal page navigations?

React js uses the virtual dom concept to update the view efficiently compared to traditional dom-manipulation technics. React updates only the necessary parts of the dom, leaving the rest of the dom unchanged. Therefore, clicking on a react <Link/> and a traditional <a href/> behave differently.

React js Link vs traditional anchor tag

React js Link is derived from the react-router-dom. Therefore, react Link is used to routing through virtual dom instead of real dom. Any changes detected during the transition are updated to the real dom. For example, the illustration below represents a page transition from page 1 to page 2 in react js. Note that the only change between the two pages is the content image. The rest of the page remains the same. React detects this change by comparing the virtual dom and re-renders only the changed portion of the dom. In this case, the content component.

React virtual DOM
React virtual DOM

Using the traditional anchor tag in place of the react Link would result in the entire page load regardless most of the page content remains the same.

Pros and cons of React Router Link

The main advantage of the react Link is performance due to efficient dom manipulation. As I mentioned earlier, the changes are compared in the virtual dom and update only necessary elements in the real dom, avoiding a total dom re-render.

One potential drawback is external javascript execution. The javascript files you import in your application and javascript code reside inside the page header or body is executed only once. That's when you enter the page by typing the URL or any way that causes a full page load. And this is also the reason why Google analytics does not track your internal page navigations.

L Raney
By: L Raney
Lance is a software engineer with over 15 years of experience in full-stack software development.

Comments are disabled

No Comments