Loading...
Loading...
Implements complete GTM tracking including dataLayer events in code and GTM configuration via API. Use when users need to "implement GTM tracking", "add dataLayer events", "create GTM variables and tags", "set up CTA tracking", "implement event tracking", or want to execute a tracking plan. Handles both code implementation (dataLayer.push) and GTM container configuration (variables/triggers/tags) automatically via API. Supports incremental updates and framework-specific patterns (React, Next.js, Vue, etc.).
npx skill4agent add aimonk2025/google-tag-manager-automation gtm-implementationCheck for gtm-tracking-plan.json (from gtm-strategy skill):
- If exists → Load events and parameters
- If missing → Ask user which events to implement OR suggest running gtm-strategy first
Check for gtm-config.json and gtm-token.json (from gtm-setup skill):
- If missing → Suggest running gtm-setup skill first
- If exists → Verify API connectionCheck package.json:
- React version
- Next.js version and router type (App Router vs Pages Router)
- Vue version
- TypeScript vs JavaScript
This determines:
- Component file extensions (.tsx vs .jsx vs .vue)
- Import patterns
- Syntax for className vs class
- 'use client' directive for Next.js App RouterUsing Glob and Grep, find elements to instrument:
For CTA tracking:
- Search for: class=".*js-cta.*" or id="cta_.*"
- Search for: <button, <Link components
For form tracking:
- Search for: class=".*js-form.*" or id="form_.*"
- Search for: <form tags with onSubmit handlers
For navigation tracking:
- Search for: class=".*js-nav.*" or id="nav_.*"
- Search for: <Link, <a tags
Match found elements against events in tracking plan.// File: app/page.tsx
<button
className="btn primary js-track js-cta js-click js-hero"
id="cta_hero_get_started"
>
Get Started
</button><button
className="btn primary js-track js-cta js-click js-hero"
id="cta_hero_get_started"
>
Get Started
</button><button
className="btn primary js-track js-cta js-click js-hero"
id="cta_hero_get_started"
onClick={() => {
if (typeof window !== 'undefined' && window.dataLayer) {
window.dataLayer.push({
event: 'cta_click',
cta_location: 'hero',
cta_type: 'primary',
cta_text: 'Get Started',
cta_destination: '/signup'
})
}
}}
>
Get Started
</button>'use client'
import { useRouter } from 'next/navigation'
export default function HeroCTA() {
const router = useRouter()
return (
<button
className="btn primary js-track js-cta js-click js-hero"
id="cta_hero_get_started"
onClick={() => {
// Track event
if (typeof window !== 'undefined' && window.dataLayer) {
window.dataLayer.push({
event: 'cta_click',
cta_location: 'hero',
cta_type: 'primary',
cta_text: 'Get Started',
cta_destination: '/signup'
})
}
// Navigate
router.push('/signup')
}}
>
Get Started
</button>
)
}<form
className="contact-form js-track js-form js-submit js-hero"
id="form_hero_contact"
onSubmit={handleSubmit}
><form
className="contact-form js-track js-form js-submit js-hero"
id="form_hero_contact"
onSubmit={handleSubmit}
><form
className="contact-form js-track js-form js-submit js-hero"
id="form_hero_contact"
onSubmit={(e) => {
// Track form submission
if (typeof window !== 'undefined' && window.dataLayer) {
window.dataLayer.push({
event: 'form_submit',
form_name: 'contact',
form_location: 'hero',
form_type: 'contact_request'
})
}
// Call original handler
handleSubmit(e)
}}
><Link href="/pricing">Pricing</Link><Link
href="/pricing"
className="nav-link js-track js-nav js-click js-header"
id="nav_header_pricing"
>
Pricing
</Link><Link
href="/pricing"
className="nav-link js-track js-nav js-click js-header"
id="nav_header_pricing"
onClick={() => {
if (typeof window !== 'undefined' && window.dataLayer) {
window.dataLayer.push({
event: 'navigation_click',
nav_location: 'header',
nav_type: 'menu_link',
nav_text: 'Pricing',
nav_destination: '/pricing'
})
}
}}
>
Pricing
</Link>// id="cta_hero_get_started" → location: "hero"
const id = element.getAttribute('id') // "cta_hero_get_started"
const location = id.split('_')[1] // "hero"// <button>Get Started</button> → cta_text: "Get Started"
cta_text: 'Get Started'// <Link href="/signup"> → cta_destination: "/signup"
// onClick={() => router.push('/pricing')} → cta_destination: "/pricing"// className includes "btn-primary" → type: "primary"
// className includes "btn-secondary" → type: "secondary"// Variable 1: CTA Location
{
name: "DLV - CTA Location",
type: "v",
parameter: [
{ type: "TEMPLATE", key: "name", value: "cta_location" },
{ type: "INTEGER", key: "dataLayerVersion", value: "2" }
]
}
// Variable 2: CTA Type
{
name: "DLV - CTA Type",
type: "v",
parameter: [
{ type: "TEMPLATE", key: "name", value: "cta_type" },
{ type: "INTEGER", key: "dataLayerVersion", value: "2" }
]
}
// Variable 3: CTA Text
{
name: "DLV - CTA Text",
type: "v",
parameter: [
{ type: "TEMPLATE", key: "name", value: "cta_text" },
{ type: "INTEGER", key: "dataLayerVersion", value: "2" }
]
}
// Variable 4: CTA Destination
{
name: "DLV - CTA Destination",
type: "v",
parameter: [
{ type: "TEMPLATE", key: "name", value: "cta_destination" },
{ type: "INTEGER", key: "dataLayerVersion", value: "2" }
]
}tagmanager.accounts.containers.workspaces.variables.create({
parent: `accounts/${accountId}/containers/${containerId}/workspaces/${workspaceId}`,
requestBody: variableConfig
}){
name: "CE - CTA Click",
type: "CUSTOM_EVENT",
customEventFilter: [
{
type: "EQUALS",
parameter: [
{ type: "TEMPLATE", key: "arg0", value: "{{_event}}" },
{ type: "TEMPLATE", key: "arg1", value: "cta_click" }
]
}
]
}tagmanager.accounts.containers.workspaces.triggers.create({
parent: `accounts/${accountId}/containers/${containerId}/workspaces/${workspaceId}`,
requestBody: triggerConfig
}){
name: "GA4 - CTA Click",
type: "gaawe", // GA4 Event tag type
parameter: [
{
type: "TEMPLATE",
key: "eventName",
value: "cta_click"
},
{
type: "LIST",
key: "eventParameters",
list: [
{
type: "MAP",
map: [
{ type: "TEMPLATE", key: "name", value: "cta_location" },
{ type: "TEMPLATE", key: "value", value: "{{DLV - CTA Location}}" }
]
},
{
type: "MAP",
map: [
{ type: "TEMPLATE", key: "name", value: "cta_type" },
{ type: "TEMPLATE", key: "value", value: "{{DLV - CTA Type}}" }
]
},
{
type: "MAP",
map: [
{ type: "TEMPLATE", key: "name", value: "cta_text" },
{ type: "TEMPLATE", key: "value", value: "{{DLV - CTA Text}}" }
]
},
{
type: "MAP",
map: [
{ type: "TEMPLATE", key: "name", value: "cta_destination" },
{ type: "TEMPLATE", key: "value", value: "{{DLV - CTA Destination}}" }
]
}
]
},
{
type: "TAG_REFERENCE",
key: "measurementId",
value: "{{GA4 Configuration Tag}}" // Reference to GA4 config tag
}
],
firingTriggerId: ["{{CE - CTA Click trigger ID}}"]
}tagmanager.accounts.containers.workspaces.tags.create({
parent: `accounts/${accountId}/containers/${containerId}/workspaces/${workspaceId}`,
requestBody: tagConfig
})// List existing variables
const existingVariables = await tagmanager.accounts.containers.workspaces.variables.list({
parent: `accounts/${accountId}/containers/${containerId}/workspaces/${workspaceId}`
})
// Check if "DLV - CTA Location" already exists
const exists = existingVariables.data.variable?.find(v => v.name === "DLV - CTA Location")
if (exists) {
// Update existing variable
tagmanager.accounts.containers.workspaces.variables.update({
path: exists.path,
requestBody: variableConfig
})
} else {
// Create new variable
tagmanager.accounts.containers.workspaces.variables.create({...})
}const version = await tagmanager.accounts.containers.workspaces.create_version({
path: `accounts/${accountId}/containers/${containerId}/workspaces/${workspaceId}`,
requestBody: {
name: `GTM Automation - ${new Date().toISOString()}`,
notes: `Automated implementation via gtm-implementation skill
Events implemented:
- cta_click (12 elements)
- form_submit (3 elements)
- navigation_click (8 elements)
Variables created: 12
Triggers created: 3
Tags created: 3`
}
})
console.log(`✓ Container version created: ${version.data.containerVersionId}`)
console.log(`→ Preview in GTM: ${version.data.containerVersion.tagManagerUrl}`)=== GTM Implementation Complete ===
--- Code Changes ---
Files modified: 8
app/page.tsx:
✓ Added CTA click tracking (3 buttons)
✓ Added form submit tracking (1 form)
components/Navbar.tsx:
✓ Added navigation click tracking (5 links)
components/Footer.tsx:
✓ Added navigation click tracking (3 links)
✓ Added form submit tracking (1 newsletter form)
... (4 more files)
--- DataLayer Events Implemented ---
✓ cta_click (12 elements tracked)
✓ form_submit (3 elements tracked)
✓ navigation_click (8 elements tracked)
Total events: 23
--- GTM Container Configuration ---
Account: 1234567890
Container: GTM-ABC1234
Workspace: Default Workspace
Created via API:
✓ 12 Data Layer Variables
✓ 3 Custom Event Triggers
✓ 3 GA4 Event Tags
Container version created: 42
Preview URL: https://tagmanager.google.com/#/versions/accounts/123/containers/456/versions/42
--- Next Steps ---
1. Test tracking in GTM Preview mode
→ Invoke gtm-testing skill for guided testing
2. Review container version in GTM:
→ Open GTM → Versions → Version 42
→ Check variables, triggers, tags
3. Publish when ready:
→ GTM → Submit → Publish
Ready to test tracking? Invoke gtm-testing skill.// Add proper typing
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
// Track
window.dataLayer?.push({...})
// Original handler
originalHandler(e)
}}try {
await tagmanager.accounts.containers.workspaces.variables.create({...})
} catch (error) {
if (error.code === 409) {
// Variable already exists - update instead
} else if (error.code === 403) {
// Permission denied
} else {
throw error
}
}// id="cta_hero_get_started"
const [category, location, ...action] = id.split('_')
// category: "cta", location: "hero", action: ["get", "started"]// className="btn btn-primary"
const isPrimary = className.includes('primary')
const type = isPrimary ? 'primary' : 'secondary'// <button>Get Started</button>
const text = element.innerText // "Get Started"// <Link href="/pricing">
const destination = href // "/pricing"
// onClick={() => router.push('/signup')}
// Parse from handler → destination: "/signup"template.mdexamples/sample.md