Web Analytics with Google Analytics 4Tagging with a tag manager · Lesson 8 of 20

The data layer and custom events

Article · 12 min · 8 min lecture

Video lecture

The data layer and custom events

15 chapters · about 8 min · full transcript

Coming soon

Chapter 1 of 15

The data layer and custom events

  • What a data layer is
  • The push pattern
  • Developer specifications
  • Timing and single-page apps
  • Contract tests

The narrated lecture is in production

Every chapter is scripted and ready. Browse the chapters and read the full transcript now — the video will appear here when it’s published.

Chapters

What is a data layer?

A data layer is a JavaScript object — in GTM, an array called dataLayer — where the website places structured information for tags to read. It decouples what happened on the site from which tools receive it. When the site pushes generate_lead, GTM can forward it to GA4, an ad platform and a CRM integration without the developer knowing about any of them.

The push pattern

Developers add information with dataLayer.push(). Pushes containing an event key can trigger tags:

window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
  event: "generate_lead",
  form_id: "contact_main",
  service: "paid_social",
  lead_type: "consultation"
});

In GTM you then create:

  • Data Layer Variables for form_id, service, lead_type.
  • A Custom Event trigger matching generate_lead.
  • A GA4 event tag with event name generate_lead and parameters mapped to those variables.

E-commerce in the data layer

For e-commerce, Google's documented structure uses an ecommerce object. Always clear the previous ecommerce object before pushing a new one so values do not leak between events:

dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: "add_to_cart",
  ecommerce: {
    currency: "GBP",
    value: 42.00,
    items: [{ item_id: "SKU-778", item_name: "Ceramic mug set", price: 21.00, quantity: 2 }]
  }
});

Most e-commerce platforms have apps or plugins that produce this structure; verify the output rather than assuming it is correct.

Writing a developer specification

Developers need precise instructions, not "please track the form". A good spec contains:

EVENT: generate_lead
WHEN:  After the server returns success for the contact form (HTTP 200 + success flag).
       Do NOT push on validation errors or network failures.
PUSH:
  event:     "generate_lead"
  form_id:   string, one of: contact_main | quote_request | newsletter_footer
  service:   string, one of: seo | paid_social | web_design | other
  lead_type: string, one of: consultation | quote
PAGES: /contact, /services/*, footer on all pages
TEST:  Submit valid form in staging -> event appears once in GTM preview with correct values.
       Submit invalid form -> no event.
NO PII: never include name, email, phone or message text.

Handing developers this level of detail saves days of back-and-forth.

Timing and ordering

Order matters. Data needed on page load (for example page_type, content_group, user_logged_in) should be pushed before the GTM container snippet or at least before the Google tag fires, so page views carry it. Interaction events are pushed when they happen.

For single-page applications (many React, Vue or Angular sites), the page does not reload between views. Developers should push a virtual page view event on route change, or you rely on GA4's history-change page views — but not both, or you will double count.

Server-side tagging (concept)

In server-side tagging, the browser sends data to a tagging server you control (for example on your own subdomain), which then forwards it to vendors. Potential benefits include more control over what data each vendor receives, the ability to strip or hash fields centrally, and fewer third-party scripts in the browser. It adds hosting cost and technical complexity, and it does not remove the need for consent: legal obligations follow the data, not the architecture. Consider it once your client-side implementation is clean and well governed.

Worked example: a quote form in a WordPress site

A logistics company in Jeddah uses a popular WordPress form plugin. The plugin redirects to a thank-you page on success. Two options:

  1. Thank-you page trigger — fire generate_lead on page view of /thank-you. Simple, but anyone who bookmarks or revisits the page creates a false lead, and you cannot see which form was used.
  2. Plugin success hook — a developer adds a small script to the plugin's success callback that pushes generate_lead with form_id and service.

Option 2 is more accurate and richer. If option 1 is the only choice, add a URL parameter from the form (such as ?form=quote) and a referrer check to reduce false positives, and document the limitation.

Hands-on: pushing from a real success callback

Most modern forms submit with JavaScript. Push only after the server confirms success:

async function submitQuote(form) {
  const res = await fetch('/api/quote', { method: 'POST', body: new FormData(form) });
  const data = await res.json().catch(() => ({}));
  if (!res.ok || !data.success) {
    showError(data.message || 'Please check the form');
    return;                                     // no analytics event on failure
  }
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({
    event: 'generate_lead',
    form_id: 'quote_request',                   // controlled values only
    service: form.elements.service.value,       // e.g., seo | paid_social | web_design
    lead_type: 'quote'
    // never: name, email, phone, message text
  });
  showThankYou();
}

Two details matter: the push happens after res.ok and the success flag, and values come from controlled lists, never from free-text fields.

Hands-on: a data layer contract test

Ask developers to include a small automated check in their end-to-end tests so a redesign cannot silently break measurement:

// Example with Playwright
await page.fill('#service', 'seo');
await page.click('#submit');
await page.waitForSelector('.thank-you');
const pushes = await page.evaluate(() => window.dataLayer.filter(e => e.event === 'generate_lead'));
expect(pushes).toHaveLength(1);
expect(pushes[0]).toMatchObject({ form_id: 'quote_request', service: 'seo' });
expect(JSON.stringify(pushes[0])).not.toMatch(/@/);   // crude guard against emails

Second worked example: a Saudi e-commerce app built as a single-page app

A Riyadh fashion retailer's site is a React single-page app. Page views were double counted because the developers pushed a virtual page view on route change while GA4's history-change page views were also on. The team chose one method (developer push with page_type and content_group), disabled the history-change option in enhanced measurement, and added a contract test for route changes. Engagement metrics stabilized, and content-group reporting finally matched the site's structure.

Common mistakes

  • Pushing PII (email, phone) "for convenience" — never do this.
  • Not clearing ecommerce between pushes, so a purchase carries items from an earlier view_item.
  • Relying on the thank-you page URL alone for important conversions.
  • Changing data layer key names without telling the analytics team — GTM variables silently go undefined.
  • Pushing events before dataLayer is initialized, overwriting it.

Data layer governance

Treat the data layer as a product interface. Version it, document it alongside the tracking plan, and include analytics checks in the site's release testing so a front-end redesign does not silently break measurement.

Key takeaways

  • The data layer separates what happened on the site from which tools receive it.
  • Give developers exact specs: when to push, keys, allowed values and test cases.
  • Clear the ecommerce object before each new e-commerce push.
  • Server-side tagging adds control, not consent exemptions.

Check your understanding

Quick questions to lock in the lesson. They don’t count towards your certificate.

  1. A purchase event in GA4 contains items the user only viewed earlier. What most likely went wrong?
  2. Why is a thank-you-page view a weaker lead trigger than a data layer push on form success?
  3. Which statement about server-side tagging is accurate?

Put it into practice

Write a developer specification for one key event on a site you know using the template in this lesson, including test cases for success and failure.

Enrol for free to save your progress

Reading is always free. Enrol to keep your place, take the final assessment and earn a verifiable certificate.