Skip to main content
Last updated: 8 Nov 2022

govuk_publishing_components: Google Analytics 4 and Google Tag Manager

This document describes the use of Google Tag Manager (GTM) with Google Analytics 4 (GA4) on GOV.UK Publishing.

No analytics code is initialised and no data is gathered without user consent.

General approach

We pass three types of data to GA4.

  • page views
  • events
  • search data

Page views happen when a page loads.

Events happen when a user interacts with certain things, for example clicking on an accordion, tab, or link.

Search data is gathered when users perform a search.

The analytics code is only loaded if users consent to cookies. This is managed by the init-ga4.js script.

If the page loads and cookie consent has already been given, the analytics code is initialised. This includes sending a page view and creating any event listeners for analytics code such as link tracking.

If the page loads and cookie consent has not been given, an event listener is created for the cookie-consent event, which is dispatched by the cookie banner component. If triggered, the event listener will initialise the analytics code as described above. This allows analytics to begin on the page where the user consents to cookies.

Code structure

It is important that no analytics code runs until cookie consent is given. Code to be initialised as part of cookie consent should be attached to the window.GOVUK.analyticsGa4.analyticsModules object and include an init function, using the structure shown below.

window.GOVUK = window.GOVUK || {}
window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {}
window.GOVUK.analyticsGa4.analyticsModules = window.GOVUK.analyticsGa4.analyticsModules || {};

(function (analyticsModules) {
  'use strict'

  var ExampleCode = {
    init: function () {
      // do analytics stuff, like send a page view

  analyticsModules.ExampleCode = ExampleCode

When cookie consent is given, init-ga4.js looks through the analyticsModules object for anything with an init function, and executes them if found. This means that analytics code will not be executed unless consent is given, and gives a standard way to add more analytics code without additional initialisation.

Code structure for Modules

Where analytics code is required as a GOV.UK JavaScript Module, the code structure for the existing model for deferred loading should be used.

There are several types of user initiated tracking. To distinguish them and simplify the code, we consider them as the following:

  • interactions with links where tracking is added using data attributes on specific links
  • interactions with specialist links, e.g. external links, download links, mailto links, where tracking is determined automatically by the content of the link
  • interactions with buttons or other interactive elements e.g. details elements, where tracking is added using data attributes on specific elements

Each type of tracking is handled by a separate script, but some code is shared between them as they do similar things.

Data schemas

All of the data sent to GTM is based on a common schema.

  event: '',
  page: {},
  event_data: {},
  search_results: {}

event must have a specific value to activate the right trigger in GTM.

page is defined in the ga4-page-views script.

event_data is defined in the ga4-schemas script and used in the ga4-event-tracker script.

search_results is defined in the ga4-ecommerce-tracker script.

How the dataLayer works

GTM's dataLayer has two elements - an array and an object. window.dataLayer = [] is executed when the page loads.

GOV.UK JavaScript (JS) pushes objects to the dataLayer array when certain things happen e.g. when the page loads, or a user clicks on a certain type of link. Once that happens GTM takes over. It reads the latest object in the array and passes the data found into the dataLayer object. Importantly, it only adds to the object, so data can persist from previous array pushes.

For example:

  • an event happens and JS pushes { a: 1 } to the dataLayer array
  • GTM adds this to the dataLayer object, which is now { a: 1 }
  • another event happens and JS pushes { b: 1 } to the array
  • GTM adds this to the dataLayer object, which is now { a: 1, b: 1 }

If data shouldn't persist it can be erased in a following push, for example by sending { a: 1, b: false }, but often this overall behaviour is desirable - for example, page view data will persist in events that happen on that page, providing more context for analysts.

If the data given to GTM contains a recognised event attribute value, GTM then sends that data on to GA4.

The dataLayer is recreated when a user navigates to another page, so no data in the dataLayer will persist between pages.