Using the Web Vitals Attribution API for Root Cause Diagnosis
AI generated
60fps
ms
Web Performance / Monitoring
Using the Web Vitals Attribution API for Root Cause Diagnosis
When a metric value alone doesn't tell you where in the code the problem lives

A bad LCP or CLS value on a monitoring dashboard tells you a problem exists, but not what's causing it. The attribution build of the web-vitals JavaScript library closes exactly that gap by reporting the concrete element or event responsible alongside every metric.

14 min read Web Vitals Attribution API

1. The Problem: A Metric Value Without a Cause

A typical real-user-monitoring dashboard shows that Largest Contentful Paint sits at 4.2 seconds at the 75th percentile on mobile devices, well above the recommended 2.5 second threshold. That number alone, however, says nothing about whether the problem is a slow-loading hero image, a delayed web font, or blocking JavaScript holding up the rendering process.

Without that information, a developer is left with tedious guesswork or manually reproducing the issue in local Chrome DevTools sessions, which often doesn't even work for production problems that only occur under specific network conditions or on specific devices. This is exactly where the attribution extension of the web-vitals library steps in, by building root cause analysis directly into the measurement taken in the user's actual browser.

2. What Is the Attribution Build of the web-vitals Library

Alongside its standard build, the web-vitals library maintained by Google offers a so-called attribution build, reachable under the import path web-vitals/attribution, which returns a structured attribution object in addition to the plain metric value. That object holds metric-specific details, for instance, for LCP a reference to the actual DOM element that was judged the largest visible element.

The extra overhead for this additional information is deliberately kept small, so the attribution build can be run in production without any noticeable impact on actual page performance. Under the hood, the library relies on existing browser APIs like PerformanceObserver and the Layout Instability API, but bundles their raw data into a much more usable structure.

3. LCP Attribution in Detail

For Largest Contentful Paint, the attribution object includes, among other fields, element with a CSS selector for the responsible element, url with the URL of the loaded resource if it's an image, and a breakdown of time spent across timeToFirstByte, resourceLoadDelay, resourceLoadDuration, and elementRenderDelay.

This breakdown allows for targeted diagnosis: a high resourceLoadDelay means the resource starts loading too late, which often points to missing preload hints. A conspicuously long resourceLoadDuration instead points to file size or server response time as the issue, while a high elementRenderDelay suggests blocking JavaScript or expensive style computation after the resource has already loaded.


import { onLCP, onCLS } from 'web-vitals/attribution';

onLCP((metric) => {
  const { element, url, timeToFirstByte, resourceLoadDelay,
          resourceLoadDuration, elementRenderDelay } = metric.attribution;

  sendToAnalytics({
    name: 'LCP',
    value: metric.value,
    element,                // e.g. "img.hero-banner"
    url,                    // resource URL, if applicable
    timeToFirstByte,
    resourceLoadDelay,
    resourceLoadDuration,
    elementRenderDelay,
    navigationType: metric.navigationType,
  });
});

onCLS((metric) => {
  const { largestShiftTarget, largestShiftValue, largestShiftTime } =
    metric.attribution;

  sendToAnalytics({
    name: 'CLS',
    value: metric.value,
    largestShiftTarget,     // CSS selector of the shifted element
    largestShiftValue,
    largestShiftTime,
  });
});

4. CLS Attribution in Detail

For Cumulative Layout Shift, the attribution object provides largestShiftTarget as a CSS selector of the element involved in the single largest layout shift, along with largestShiftValue for that individual shift's score contribution and largestShiftTime for the exact point in time within the page load.

Since CLS is often caused by several small shifts throughout a page's lifetime, focusing on the single largest shift is a deliberate trade-off between detail and data volume, but in practice it reliably surfaces the dominant culprit. That's often a late-loading ad banner, an image without a reserved aspect ratio, or a cookie consent banner inserted after the fact.

5. Practical Debugging Example: A Bad LCP Value

Suppose monitoring shows a bad LCP value for an online store's product detail page. The attribution data shows img.product-main-image as the element, a low timeToFirstByte, but a conspicuously high resourceLoadDelay of over eight hundred milliseconds, while the actual resourceLoadDuration looks unremarkable.

This combination suggests the main product image could actually be served quickly by the server, but the download start is being needlessly delayed, typically because the image only gets inserted into the DOM after blocking JavaScript runs, instead of appearing directly in the initial HTML. The fix is usually to render the image into the initial markup server-side and additionally request it early with <link rel="preload">.

6. Practical Debugging Example: A Bad CLS Value

In a second example, monitoring reports an elevated CLS value on a category page. Attribution shows div.newsletter-banner as the largestShiftTarget, with a largestShiftTime of roughly three seconds after page start, well after the initial rendering of visible content.

This pattern is typical of delayed, lazily loaded marketing components like newsletter banners or consent dialogs that get inserted into the existing page flow without reserved space, pushing visible content downward. The fix is usually reserving a fixed minimum height for the container from the start via CSS, so the later insertion no longer causes any shift, even if the component itself only gets populated later.

7. Integration into Real User Monitoring

For production use, attribution data is typically sent to an in-house analytics endpoint or a commercial RUM provider via the sendBeacon API or a fetch call with the keepalive flag, as soon as the browser finalizes the metric. It's important not to delay the send by doing heavy processing on the main thread, so the reporting itself doesn't add to the very performance burden being measured.

The collected attribution data can then be grouped by selector, page type, or device class to spot systematic patterns rather than one-off cases. If the same element selector keeps showing up as the leading cause of bad LCP values week after week, that's a reliable signal for where a targeted optimization is actually worth the effort, instead of investing resources based on guesswork.

8. Sampling and Data Volume Considerations

On high-traffic websites, sending attribution data for every single page view can generate a substantial data volume, both on the client side and at the analytics backend. A common approach is a sampling factor where, for example, only ten percent of page views send the full attribution payload, while the plain metric values continue to be captured for every view.

For critical page types like checkout, a higher sampling rate or even full capture often makes sense instead, since even small performance issues there can have a direct impact on conversion rate. The specific sampling strategy should be reviewed and adjusted regularly based on the actual data volume and the cost of the analytics backend.

9. Summary and Practical Recommendation

The attribution build of the web-vitals library turns abstract metric values into concretely actionable diagnostic information, by reporting the responsible element and relevant time breakdown alongside every measured metric. That significantly shortens the path from a flagged dashboard value to a targeted code change, without first having to painstakingly reproduce the issue manually.

To get started, it's worth enabling the attribution build with a moderate sampling factor on the most important page types first, and regularly scanning the collected data for recurring patterns. Over time, that builds a data-driven understanding of which concrete elements and loading phases are actually responsible for poor user experience in production.

Metric Attribution Field Typical Cause Diagnostic Hint
LCP High resourceLoadDelay Resource starts loading too late Missing preload or late DOM insertion
LCP High resourceLoadDuration Large file or slow server Check image compression, CDN, server time
LCP High elementRenderDelay Blocking JavaScript or CSS Check render path and script execution
CLS Late largestShiftTarget Lazily loaded component without reserved space Reserve a fixed minimum height via CSS

Mironsoft

Web performance, Core Web Vitals, and load time optimization

Load times that don't make users bounce before the page is even visible?

We review existing websites for slow Core Web Vitals, bloated JavaScript bundles, and unnecessary render blockers, then build a performance foundation that stays measurable instead of just looking good once.

Performance Audit

Systematically measuring and fixing Core Web Vitals, load waterfall, and render blockers.

Bundle Optimization

Specifically reducing JavaScript and CSS bundle size and improving code splitting.

Monitoring Setup

Establishing continuous performance monitoring instead of a one-time snapshot.

10. Zusammenfassung

Web Vitals Attribution API

Technique

Attribution build of web-vitals reports cause, not just a number

Key fields

element, resourceLoadDelay, largestShiftTarget

Use case

Integration into real user monitoring via sendBeacon

Benefit

Root cause analysis directly in the real user's browser

11. FAQ: Web Vitals Attribution API

1What does the attribution build of web-vitals provide beyond the standard build?
Beyond the plain metric value, the attribution build returns a structured object with details about the cause, such as the responsible DOM element for LCP or the shifted element for CLS. The standard build only returns the bare number.
2How do you import the attribution build?
The attribution build is imported through the separate path web-vitals/attribution instead of the standard import from web-vitals. Function names like onLCP or onCLS stay identical.
3Which fields does LCP attribution provide?
For LCP, attribution provides element as a CSS selector, url of the loaded resource, and a time breakdown across timeToFirstByte, resourceLoadDelay, resourceLoadDuration, and elementRenderDelay.
4What does a high resourceLoadDelay mean for LCP?
A high resourceLoadDelay means the LCP resource starts downloading later than necessary, often because it only appears in the DOM after blocking JavaScript runs. The usual fix is placing the resource early in the HTML or requesting it via preload.
5How does largestShiftTarget help with CLS diagnosis?
largestShiftTarget provides the CSS selector of the element involved in the single largest layout shift. That makes it possible to identify the exact container that needs reserved space, instead of trying every possible cause one by one.
6Does the attribution build itself hurt page performance?
The added overhead is deliberately kept small, since the library builds on existing browser APIs like PerformanceObserver and simply structures their raw data. No noticeable impact on measured performance is expected in practice.
7How do you send attribution data to an analytics system?
A common approach is sending it via the sendBeacon API or a fetch call with the keepalive flag as soon as the browser finalizes the metric. That ensures the data gets transmitted reliably even during an immediately following page navigation.
8Should attribution data be sent for every single page view?
On high-traffic sites, a sampling factor is usually recommended, capturing full attribution data for only a portion of page views while plain metric values continue to be collected for all views. Critical page types like checkout, on the other hand, often benefit from a higher sampling rate.
9Does the attribution API work for metrics other than LCP and CLS?
Yes, the attribution build provides comparable detail data for metrics like INP and FCP as well, each with its own metric-specific fields. The underlying principle of pairing the number with concrete cause information applies to every supported metric.
10Does the attribution API replace local debugging in Chrome DevTools?
No, it meaningfully complements it by providing root cause data straight from real production sessions that often can't be reproduced locally at all. For a deep dive into a concretely identified issue, DevTools remains an important tool.