The Core Web Vitals custom tag template is a tag template for Google Tag Manager’s community template gallery.

Resource
Blog post
Vendor documentation
Gallery entry
GitHub repo

Description

This tag adds measurement handlers for three Core Web Vitals: Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS).

There is also a toggle (Collect all (available) web vitals metrics) that enables the collection of other supported Web Vitals metrics: FCP, INP, and TTFB.

Each measurement is pushed into dataLayer as soon as it is available.

Note that not every page load will register all three milestones. Read the vendor documentation to understand how web-vitals works.

Instructions

The template has three configurable options.

  1. Namespace Objects. If this is checked, then all measurements will be nested under an object with the key matching the respective metric name (see below for an example).

  2. Collect all (available) web vitals metrics. If this is checked, then also FCP (First Contentful Paint) and TTFB (Time To First Byte) are collected.

  3. Send attribution data. Check this to enable the attribution build. When enabled, the CLS, INP, and LCP metrics include a CSS selector that points to the target of the measurement (when available). This might help you debug potential bottlenecks.

You can set this tag to fire as early as possible, ideally on the Initialization trigger. This tag doesn’t collect or process any data itself. It just adds listeners for the different metrics and pushes the results into dataLayer. Thus you can have this tag fire even if the user has not (yet) interacted with your site’s consent banner.

The tag writes the following objects into dataLayer for each time they are measured (CLS, for example, can be measured more than once per page).

{
  event: 'coreWebVitals',
  webVitalsMeasurement: {
    debugTarget: '#main', // From the attribution build
    delta: 8,
    deltaRounded: 8,
    id: 'v5-1746686331073-9529501018378',
    loadState: 'complete', // From the attribution build
    name: 'INP',
    navigationType: 'navigate',
    rating: 'good',
    value: 8,
    valueRounded: 8
  }
}

If you have Namespace Objects checked, then the measurement above would be pushed to dataLayer like this instead:

{
  event: 'coreWebVitals',
  webVitalsMeasurement: {
    LCP: {
      debugTarget: '#main',
      delta: 8,
      deltaRounded: 8,
      id: 'v5-1746686331073-9529501018378',
      loadState: 'complete',
      name: 'INP',
      navigationType: 'navigate',
      rating: 'good',
      value: 8,
      valueRounded: 8    
    }
  }
}

Namespacing the measurements like this is useful if you want to send all the current CWV measurements in a single request. If you don’t namespace the objects, then each subsequent measurement will overwrite the keys and values of the previous measurement.

You can create a Custom Event trigger for coreWebVitals to fire your tag when this dataLayer.push() happens.

You need to create Data Layer variables for all the webVitalsMeasurement.* variable names that you want to utilize in your tags.

Release notes

Date Changeset
8 May 2025 Removed FID as it was deprecated from v5.0.0 of the web-vitals library.
21 January 2021 Add rounded integers to the object for easier implementation.
21 January 2021 Initial release.