This guide covers implementing Google Tag Manager (GTM) on Adobe Experience Manager for both AEMaaCS and AEM 6.5.
Prerequisites
Create a GTM Container
- Sign in to tagmanager.google.com
- Create a new container (Web)
- Copy your Container ID (format:
GTM-XXXXXXX)
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
- Navigate to Tools > Cloud Services > Google Tag Manager
- Create new configuration:
- Title: GTM Configuration
- Container ID: GTM-XXXXXXX
- Apply to site via Page Properties > Cloud Services
AEM 6.5
- Go to Tools > Deployment > Cloud Services
- Find Google Tag Manager
- Click Configure Now
- 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
- Open GTM and click Preview
- Enter your AEM Publish URL
- Navigate through pages
- 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;"
}