Google Tag Manager on Adobe Experience Manager | OpsBlu Docs

Google Tag Manager on Adobe Experience Manager

Step-by-step Google Tag Manager installation and configuration guide for Adobe Experience Manager (AEM).

This guide covers implementing Google Tag Manager (GTM) on Adobe Experience Manager for both AEMaaCS and AEM 6.5.

Prerequisites

  1. Create a GTM Container

    • Sign in to tagmanager.google.com
    • Create a new container (Web)
    • Copy your Container ID (format: GTM-XXXXXXX)
  2. AEM Access

    • Author admin access
    • Access to /apps for component development

Implementation Methods

Method 1: HTL Component

Create GTM Component

/apps/mysite/components/analytics/gtm/gtm.html

<sly data-sly-use.gtm="${'com.mysite.core.models.GTMModel'}"
     data-sly-test="${gtm.enabled && gtm.publishMode}">

    <!-- Google Tag Manager -->
    <script data-sly-unwrap>
        (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.containerId}');
    </script>
</sly>

/apps/mysite/components/analytics/gtm/gtm-noscript.html

<sly data-sly-use.gtm="${'com.mysite.core.models.GTMModel'}"
     data-sly-test="${gtm.enabled && gtm.publishMode}">

    <!-- Google Tag Manager (noscript) -->
    <noscript>
        <iframe src="https://www.googletagmanager.com/ns.html?id=${gtm.containerId}"
                height="0" width="0" style="display:none;visibility:hidden">
        </iframe>
    </noscript>
</sly>

Create Sling Model

GTMModel.java

package com.mysite.core.models;

import com.day.cq.wcm.api.WCMMode;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.apache.sling.models.annotations.injectorspecific.Self;

@Model(adaptables = SlingHttpServletRequest.class)
public class GTMModel {

    @Self
    private SlingHttpServletRequest request;

    @OSGiService
    private GTMConfigService configService;

    public boolean isEnabled() {
        return configService.isEnabled();
    }

    public String getContainerId() {
        return configService.getContainerId();
    }

    public boolean isPublishMode() {
        return WCMMode.fromRequest(request) == WCMMode.DISABLED;
    }
}

Include in Page Template

In your page component's customheaderlibs.html:

<!-- Initialize dataLayer before GTM -->
<sly data-sly-include="/apps/mysite/components/analytics/datalayer/datalayer-init.html"/>

<!-- GTM head script -->
<sly data-sly-include="/apps/mysite/components/analytics/gtm/gtm.html"/>

In your page component's body (after opening <body> tag):

<sly data-sly-include="/apps/mysite/components/analytics/gtm/gtm-noscript.html"/>

Method 2: Cloud Service Configuration

AEM as a Cloud Service

  1. Navigate to Tools > Cloud Services > Google Tag Manager
  2. Create new configuration:
    • Title: GTM Configuration
    • Container ID: GTM-XXXXXXX
  3. Apply to site via Page Properties > Cloud Services

AEM 6.5

  1. Go to Tools > Deployment > Cloud Services
  2. Find Google Tag Manager
  3. Click Configure Now
  4. Enter Container ID

Data Layer Implementation

Create Data Layer Component

/apps/mysite/components/analytics/datalayer/datalayer-init.html

<sly data-sly-use.page="${'com.mysite.core.models.PageDataModel'}">
    <script data-sly-unwrap>
        window.dataLayer = window.dataLayer || [];

        // Base page data
        dataLayer.push({
            'pageType': '${page.pageType}',
            'pageName': '${page.pageName}',
            'pageLanguage': '${page.language}',
            'siteName': '${page.siteName}'
        });

        // User data
        <sly data-sly-test="${page.userLoggedIn}">
        dataLayer.push({
            'userLoggedIn': true,
            'userType': '${page.userType}'
        });
        </sly>
        <sly data-sly-test="${!page.userLoggedIn}">
        dataLayer.push({
            'userLoggedIn': false,
            'userType': 'anonymous'
        });
        </sly>
    </script>
</sly>

Page Data Model

PageDataModel.java

package com.mysite.core.models;

import com.day.cq.wcm.api.Page;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.Self;

@Model(adaptables = SlingHttpServletRequest.class)
public class PageDataModel {

    @ScriptVariable
    private Page currentPage;

    @Self
    private SlingHttpServletRequest request;

    public String getPageType() {
        String template = currentPage.getTemplate().getPath();
        if (template.contains("home")) return "home";
        if (template.contains("product")) return "product";
        if (template.contains("category")) return "category";
        return "content";
    }

    public String getPageName() {
        return currentPage.getTitle();
    }

    public String getLanguage() {
        return currentPage.getLanguage().getLanguage();
    }

    public String getSiteName() {
        return currentPage.getAbsoluteParent(1).getTitle();
    }

    public boolean isUserLoggedIn() {
        return request.getUserPrincipal() != null &&
               !"anonymous".equals(request.getUserPrincipal().getName());
    }

    public String getUserType() {
        // Implement based on your user groups
        return "registered";
    }
}

Experience Fragment Integration

For Experience Fragments used across pages:

/apps/mysite/components/analytics/tracking-fragment/tracking-fragment.html

<sly data-sly-use.fragment="${'com.mysite.core.models.TrackingFragmentModel'}">
    <sly data-sly-test="${fragment.shouldTrack}">
        <script data-sly-unwrap>
            dataLayer.push({
                'event': 'fragment_view',
                'fragmentId': '${fragment.fragmentId}',
                'fragmentName': '${fragment.fragmentName}'
            });
        </script>
    </sly>
</sly>

Verification

GTM Preview Mode

  1. Open GTM and click Preview
  2. Enter your AEM Publish URL
  3. Navigate through pages
  4. Verify tags fire correctly

Author vs Publish

Ensure tracking only fires on Publish:

public boolean isPublishMode() {
    WCMMode mode = WCMMode.fromRequest(request);
    return mode == WCMMode.DISABLED;
}

Debug in Console

// Verify GTM loaded
console.log('GTM:', typeof google_tag_manager);

// Check dataLayer
console.log('dataLayer:', JSON.stringify(dataLayer, null, 2));

Dispatcher Considerations

Allow GTM domains in CSP:

dispatcher.any

/headers {
    "Content-Security-Policy" "script-src 'self' 'unsafe-inline' https://www.googletagmanager.com; connect-src 'self' https://www.google-analytics.com;"
}

Next Steps