Install Google Tag Manager on HubSpot
Google Tag Manager (GTM) provides a centralized platform to manage all your tracking tags on HubSpot without editing code repeatedly. This guide covers HubSpot-specific GTM implementation.
Why Use GTM on HubSpot?
Benefits Over Direct Code Implementation
- Easier management: Update tags without editing HubSpot settings
- Non-technical friendly: Marketers can add/modify tags independently
- Centralized control: All tags in one platform
- Better testing: Preview mode before publishing changes
- Version control: Roll back to previous container versions
- Better performance: Single container load vs. multiple scripts
- Advanced features: Custom triggers, variables, and data layer
GTM vs. Direct HubSpot Implementation
| Feature | GTM | Direct Code in HubSpot |
|---|---|---|
| Ease of updates | Click and publish | Edit Site Header HTML |
| Learning curve | Medium | Low |
| Flexibility | Very High | Medium |
| Multiple tags | Easy | Requires multiple code blocks |
| Team collaboration | Built-in | Must share HubSpot access |
| Testing | Preview mode | Production only |
Prerequisites
- Google Tag Manager account - Create free at tagmanager.google.com
- HubSpot access - Settings access to modify Site Header HTML
- Super Admin or Website Editor - HubSpot permissions
Installation Methods
Method 1: Site Header HTML (Recommended)
Install GTM globally on all HubSpot pages using Site Header HTML.
Step 1: Create GTM Container
- Go to Google Tag Manager
- Click Create Account (if new) or select existing account
- Fill in Account Name (e.g., "Your Company Name")
- Container Setup:
- Container Name: Your website domain (e.g., "example.com")
- Target Platform: Web
- Click Create
- Accept Terms of Service
Step 2: Copy GTM Container Code
After creating container, you'll see two code snippets:
Code Snippet 1 (Head):
<!-- 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 -->
Code Snippet 2 (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) -->
Copy both snippets.
Step 3: Add GTM to HubSpot Site Header
- In HubSpot, go to Settings (gear icon)
- Navigate to Website → Pages
- Scroll to Site Header HTML
- Paste the first snippet (Head code) into Site Header HTML box
- Click Save
Your Site Header HTML should look like:
<!-- 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 -->
Step 4: Add GTM Noscript to Site Footer
The noscript tag should go at the top of the <body>. Since HubSpot doesn't provide body-level code injection, add to Site Footer HTML instead:
- In same Settings → Website → Pages section
- Scroll to Site Footer HTML
- Paste the second snippet (noscript code)
- Click Save
Note: Footer placement means it won't fire if JavaScript is disabled, but this affects less than 1% of users.
Step 5: Verify Installation
- Visit your HubSpot website
- Open browser DevTools (F12)
- Go to Console tab
- Type:
google_tag_manager - Should return GTM object (not undefined)
Alternative verification:
- Install Google Tag Assistant browser extension
- Visit your site
- Extension should show GTM container ID
Method 2: Custom Template Module (Advanced)
For multi-site HubSpot setups or granular control.
Create GTM Module
- Go to Design Tools in HubSpot
- Create New Module → "GTM Container"
- Add module code:
{% if module.container_id %}
<!-- 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','{{ module.container_id }}');</script>
<!-- End Google Tag Manager -->
{% endif %}
- Add module field:
{
"id": "container_id",
"name": "container_id",
"label": "GTM Container ID",
"type": "text",
"default": "GTM-XXXXXX"
}
- Add module to template before
</head>
Setting Up GTM After Installation
Create Your First Tag: GA4 Configuration
- In GTM, go to Tags → New
- Click Tag Configuration
- Choose Google Analytics: GA4 Configuration
- Enter Measurement ID (G-XXXXXXXXXX)
- Triggering: Select All Pages
- Name tag "GA4 - Configuration"
- Save
Create Data Layer Variables
To access HubSpot data in GTM:
- In GTM, go to Variables → New
- Variable Configuration → Data Layer Variable
- Data Layer Variable Name:
pageType - Save as "Page Type"
See GTM Data Layer for HubSpot-specific data layer implementation.
Enable Built-In Variables
- In GTM, go to Variables
- Under Built-In Variables, click Configure
- Enable useful variables:
Publish Container
- Click Submit in GTM
- Version Name: "Initial setup - GA4"
- Version Description: "Added GA4 configuration tag"
- Click Publish
HubSpot-Specific GTM Configuration
Create HubSpot Custom Variables
Useful variables for HubSpot sites:
1. Contact ID Variable
function() {
return '{{ contact.vid }}' !== '' ? '{{ contact.vid }}' : null;
}
2. Lifecycle Stage Variable
function() {
return '{{ contact.lifecycle_stage }}' || 'unknown';
}
3. Content Type Variable
function() {
return '{{ content.type }}' || 'page';
}
To create Custom JavaScript variables in GTM:
- Variables → New → User-Defined Variables
- Choose Custom JavaScript
- Paste function code
- Save
Create HubSpot Event Triggers
Form Submission Trigger:
- Triggers → New
- Type: Custom Event
- Event name:
hubspot_form_submit - Save as "HubSpot Form Submission"
CTA Click Trigger:
- Triggers → New
- Type: Click - All Elements
- Fire on: Some Clicks
- Click Classes contains
hs-ctaOR Click Classes containscta_button - Save as "HubSpot CTA Click"
See Event Tracking for event listener code.
Testing GTM on HubSpot
Use GTM Preview Mode
- In GTM, click Preview (top right)
- Enter your HubSpot site URL
- Click Connect
- New tab opens with Tag Assistant connected
- Navigate your HubSpot site
- See which tags fire on each page
Verify Tags Fire Correctly
In Preview mode, check:
- Tags Fired: Should see your GA4 config tag
- Tags Not Fired: Should be empty (or expected)
- Data Layer: Verify variables populated correctly
Debug Common Issues
GTM Not Loading:
// Check in browser console
console.log(window.google_tag_manager); // Should return object
console.log(dataLayer); // Should return array
Tags Not Firing:
- Check trigger conditions
- Verify container is published (not just preview)
- Clear browser cache
- Disable ad blockers
Advanced: Multiple GTM Containers
For complex setups (e.g., separate containers for dev/staging/prod):
Environment-Based Containers
{% if content.domain == 'www.yoursite.com' %}
<!-- Production GTM -->
<script>...GTM-PROD...</script>
{% elif content.domain == 'staging.yoursite.com' %}
<!-- Staging GTM -->
<script>...GTM-STAGING...</script>
{% else %}
<!-- Development GTM -->
<script>...GTM-DEV...</script>
{% endif %}
Multiple Containers (Not Recommended)
If absolutely necessary:
<!-- Primary GTM -->
<script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
<!-- Secondary GTM (different dataLayer name) -->
<script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer2','GTM-YYYYYY');</script>
Note: This can cause conflicts. Only use if teams manage separate containers.
Integrating GTM with HubSpot Analytics
Avoid Duplicate Tracking
With both HubSpot analytics and GTM:
- HubSpot tracks natively: Page views, form submissions, CTA clicks
- GTM tracks for external tools: GA4, Meta Pixel, etc.
Best practice: Accept both systems track the same events; use each for its strengths.
Pass HubSpot Data to GTM
Add to Site Header HTML (before GTM code):
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
{% if contact %}
'contactId': '{{ contact.vid }}',
'lifecycleStage': '{{ contact.lifecycle_stage }}',
'isContact': true,
{% else %}
'isContact': false,
{% endif %}
'pageType': '{{ content.type }}',
'contentId': '{{ content.id }}',
'pageName': '{{ content.name }}'
});
</script>
<!-- Then GTM code -->
<script>(function(w,d,s,l,i){...})</script>
See GTM Data Layer for complete implementation.
Performance Optimization
Async Loading (Already Default)
GTM loads asynchronously by default:
j.async=true;
Defer Non-Critical Tags
In GTM, for non-critical tags:
- Edit tag
- Advanced Settings → Tag firing options
- Choose Once per page (instead of unlimited)
Monitor Performance Impact
Use PageSpeed Insights:
- Test page with GTM
- Check "Reduce JavaScript execution time"
- Verify GTM not blocking render
See Performance Troubleshooting for optimization.
Common GTM Tags for HubSpot
1. GA4 Configuration Tag
- Tag Type: Google Analytics: GA4 Configuration
- Trigger: All Pages
- Purpose: Initialize GA4 tracking
2. GA4 Event Tag - Form Submissions
- Tag Type: GA4 Event
- Event Name: generate_lead
- Trigger: HubSpot Form Submission
- Purpose: Track form leads
3. Meta Pixel Tag
- Tag Type: Custom HTML
- Trigger: All Pages
- Purpose: Facebook/Instagram advertising
See Meta Pixel Setup.
4. LinkedIn Insight Tag
- Tag Type: Custom HTML
- Trigger: All Pages
- Purpose: LinkedIn advertising
5. Hotjar Tracking Code
- Tag Type: Custom HTML
- Trigger: All Pages
- Purpose: User behavior analytics
Troubleshooting
GTM Container Not Loading
Symptoms: Tags don't fire, Preview mode can't connect
Check:
- Container ID is correct (GTM-XXXXXX format)
- Code is in Site Header HTML (not footer)
- HubSpot settings have been saved
- No JavaScript errors blocking GTM load
- Ad blocker disabled (for testing)
Debug:
// In browser console
console.log(google_tag_manager); // Should return object
console.log(google_tag_manager['GTM-XXXXXX']); // Your container ID
Preview Mode Can't Connect
Issue: "Tag Assistant" can't connect to site
Solutions:
- Disable browser extensions (especially privacy/ad blockers)
- Use incognito/private browsing
- Clear browser cache
- Ensure you're on the correct domain
- Check HubSpot domain is published and accessible
Tags Fire in Preview But Not Production
Cause: Container not published
Fix:
- In GTM, check top right corner
- Should say "Latest Workspace Changes Published"
- If not, click Submit → Publish
Data Layer Variables Return Undefined
Cause: Data layer not pushed before GTM loads
Fix: Ensure data layer code comes BEFORE GTM code:
<!-- Data Layer first -->
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({...});
</script>
<!-- Then GTM -->
<script>(function(w,d,s,l,i){...})</script>
Next Steps
- Configure GTM Data Layer - Pass HubSpot data to GTM
- Set up GA4 via GTM - Track analytics
- Install Meta Pixel via GTM - Facebook advertising
For general GTM concepts, see Google Tag Manager Guide.