Analytics Lab / Tag management

Google Tag Manager sandbox

Google Tag Manager reads events from window.dataLayer. Each dataLayer.push() with an event key is evaluated against the container's triggers, and matching tags fire. The controls on this page push events; the console on the right shows each push and the trigger that would match it.

Container ID
GTM-XXXXXXX
Data layer schema
universal-data-layer/v1

Base data layer

Push session, user, and page values before the container snippet loads. Data Layer Variables in GTM can then read them on every event without each tag redefining them.

Session

Created on the first page of a visit and kept in session storage.

{
  "session": {
    "id": "c2c...",
    "timestamp": "2024-01-01T12:00:00Z",
    "source": "organic"
  }
}

User

Non-identifying traits. Do not push email addresses or other PII.

{
  "user": {
    "authState": "anonymous",
    "persona": "marketer",
    "role": null
  }
}

Page

Describes the current page so reports group it consistently.

{
  "page": {
    "name": "gtm-sandbox",
    "category": "analytics-lab",
    "language": "en-US"
  }
}

Each event adds an interaction object with the element, module, and any value captured at the time of the interaction. In GTM, read nested keys with a Data Layer Variable using dot notation, for example interaction.element.

Triggers, variables, and tags

  1. Push. The page calls dataLayer.push({ event: 'cta_click', ... }). GTM merges the keys into its data model.
  2. Trigger. A Custom Event trigger matches the event value. Built-in triggers cover Click (All Elements, Just Links), Form Submission, History Change, Element Visibility, Scroll Depth, and Timer.
  3. Variable. Data Layer Variables read pushed keys. Built-in variables expose Click URL, Click Text, Form ID, Page Path, Scroll Depth Threshold, and similar values.
  4. Tag. The tag (for example a GA4 Event tag) fires with parameters filled from variables, subject to consent settings.

Tracked interactions

Each control carries data-gtm-* attributes. A small script reads them and pushes an event. Where GA4 has a recommended event name, the example uses it.

Primary CTA

Event: cta_click · Trigger: Custom Event

Demo request form

Event: generate_lead · Trigger: Custom Event

Push the event after the form validates and submits, not on button click.

Main navigation

Event: nav_select · Trigger: Custom Event

Records which top-level navigation items visitors use.

Pricing

Site search

Event: search · Trigger: Custom Event

Pushes the search term. In GA4, send it as the search_term parameter.

Video progress

Event: video_progress · Trigger: Custom Event

Progress events at 10, 25, 50, 75, and 90 percent separate partial views from completions. GA4 uses the video_percent parameter.

File download

Event: file_download · Trigger: Custom Event

Without a push, a Click - Just Links trigger filtered on Click URL ending in .pdf does the same job.

Download pricing sheet (PDF)

Consent toggle

Event: consent_update · Consent Mode v2

Changing the toggle pushes a custom event and calls gtag('consent', 'update', ...) with ad_storage, ad_user_data, and ad_personalization.

Scroll depth

Event: scroll · Trigger: Custom Event

This marker pushes once when it is fully visible. Without a push, a Scroll Depth trigger set to 90% vertical does the same. GA4 enhanced measurement sends scroll at 90% by default.

Scroll marker (90%)

Modal

Event: modal_open · Trigger: Custom Event

Tracks the modal opening and the button used to close it.

Modal closed.

FAQ accordion

Event: accordion_toggle · Trigger: Custom Event

Pushes the open or closed state. Use it to see which questions visitors expand.

Accordion collapsed.

Inline tooltip

Event: tooltip_toggle · Trigger: Custom Event

Records whether the tooltip is shown or hidden, to measure which help text gets used.

Tooltip hidden.

Copy link

Event: share · Trigger: Custom Event

Copying a link sends the GA4 share event with method: 'copy'.

Nothing copied yet.

Theme preference

Event: preference_update · Trigger: Custom Event

Switches the preview theme and pushes the previous and new values.

Current theme: Light

Theme set to light.

Onboarding steps

Events: tutorial_begin / tutorial_step / tutorial_complete

tutorial_begin and tutorial_complete are GA4 recommended events. tutorial_step is a custom event for the steps between them.

  1. Create workspace
  2. Install tracking snippet
  3. Verify first event

No steps logged.

Implementation checklist

  1. 1. Write a tracking plan: event names, parameters, and types. Use GA4 recommended names (generate_lead, search, add_to_cart, purchase) where they fit.
  2. 2. Set Consent Mode v2 defaults with gtag('consent', 'default', ...) before the container snippet.
  3. 3. Push session, user, and page values before the container snippet.
  4. 4. Push ecommerce events as { event: 'add_to_cart', ecommerce: { items: [...] } }, and push { ecommerce: null } first so items from the previous event do not carry over.
  5. 5. Create Data Layer Variables, Custom Event triggers, and tags (GA4, ad platforms) in the container.
  6. 6. Test in Preview mode and GA4 DebugView, including consent states, before publishing a version.

Container snippet

Place the consent defaults and the container script as high in <head> as possible. Place the <noscript> iframe immediately after the opening <body> tag. Replace GTM-XXXXXXX with your container ID.

&lt;script&gt;
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  // Consent Mode v2 defaults. Update them when the visitor makes a choice.
  gtag('consent', 'default', {
    ad_storage: 'denied',
    ad_user_data: 'denied',
    ad_personalization: 'denied',
    analytics_storage: 'denied',
    wait_for_update: 500
  });
&lt;/script&gt;
&lt;script&gt;
  (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'? '&amp;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-XXXXXXX');
&lt;/script&gt;
&lt;noscript&gt;
  &lt;iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX" height="0" width="0" style="display:none;visibility:hidden"&gt;&lt;/iframe&gt;
&lt;/noscript&gt;