--- title: "Blog" description: "Read about the background of our monitoring solution, new features and case studies" canonical: "https://www.rumvision.com/blog/google-web-vitals-v4-supports-loaf-inp-breakdown/" --- Breadcrumbs: [Home](https://www.rumvision.com/?format=md) > Blog # Google Chrome web-vitals.js v4 to support LoAF + INP breakdown Google Chrome released a beta version of web-vitals.js (v4 beta) with a focus on INP and LoAF. We gave it a spin. It's no secret that RUMvision is built on top of the Google Chrome's official [web-vitals library](https://www.rumvision.com/help-center/releases/changelog/web-vitals-library/?format=md). Google's library aims to mimic the Core Web Vitals as much as possible, allowing site owners to track the vital user experience metrics themselves. Either by directly using web-vitals or by utilizing a SaaS provider like ours. ## Main changes in web-vitals.js v4 While being in beta, [Barry Pollard is stating](https://twitter.com/tunetheweb/status/1774049972251451410) that web-vitals.js v4 includes a couple of exciting new additions: - INP breakdown phases - Long Animation Frame API information for the INP interaction - Renaming of attribution related object keys - `getXXX()` functions were already deprecated before and now removed in v4 - Deprecated the `onFID()` function (but it was not yet removed) ## Using web-vitals.js in DevTools You might be using Google's official [web-vitals Chrome extension](https://chromewebstore.google.com/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma) already. It's not really that different. As a matter of fact, the extension was a bit ahead of the JS library as it has been exposing INP breakdown information already. Nevertheless, you might want to give the JS library a spin as well. There are different ways to do so, but here's a quick start: ``` (function() { var script = document.createElement('script'); script.src = 'https://unpkg.com/web-vitals@4.0.0-beta.1/dist/web-vitals.attribution.iife.js'; script.onload = function() { webVitals.onINP( console.log ); } document.head.appendChild(script); }()); ``` Copy and paste this into the JavaScript console in your DevTools to observe how metric information is displayed. > Using the attribution build is a crucial step to obtain breakdown information and Long Animation Frame entries. The console.log function is passed as callback to directly log the result of the [INP metric](https://www.rumvision.com/blog/interaction-to-next-paint/?format=md) to the console. You can pass your own callback function to perform additional tasks based on the metric's data, and then submit it [to your endpoint](https://github.com/GoogleChrome/web-vitals#send-the-results-to-an-analytics-endpoint). ### Exposed metrics In the example above, I specifically used `onINP` as version 4 of the web-vitals.js library v4 especially comes with INP and LoAF additions. However, you could add other metrics as well. You could do this manually for `onTTFB`, `onFCP`, `onLCP` and `onCLS`. Alternatively, you could loop over available functions and when the function name starts with an 'on' prefix and only then hook into that function and pass your callback to it. You would then end up with the following: ``` (function() { var script = document.createElement('script'); script.src = 'https://unpkg.com/web-vitals@4.0.0-beta.1/dist/web-vitals.attribution.iife.js'; script.onload = function() { Object.keys(webVitals).forEach(function(fn) { fn.indexOf('on') === 0 && webVitals[fn](console.log); }); } document.head.appendChild(script); }()); ``` > Fun fact: the above is what RUMvision is doing to hook into all available metrics. However, despite still being available in both web-vitals JS version 3 as well as 4 we did [stop tracking FID](https://www.rumvision.com/blog/replacing-fid-fid-to-be-removed-from-google-tools/?format=md) because of its deprecated status. ### Moment of exposure Please note that you might not get to see as much metrics as you would expect. This is because not all metrics are immediately reported. It is important to note that this behavior isn't specific to v4. It used to be like this for all versions of web-vitals JS. #### **TTFB & LCP** TTFB and FCP are reported as soon as they occur. However, there might be a slight delay on websites with more JavaScript or console activity, as the browser needs to be idle enough to report them to the console. #### **LCP** LCP is reported as soon as an interaction happens, as that is when even Google's actual Core Web Vitals stops listening for new LCP candidates. This means you would first need to click within the page's viewport before seeing the LCP being reported in your DevTools console. Read about all [LCP nuances and characteristics](https://www.rumvision.com/help-center/glossary/metrics/largest-contentful-paint/?format=md). #### **FID** Although deprecated, you would see the same effect for **FID**. #### **CLS and INP** If you think the above LCP behaviour is unexpected, then you might really be surprised to not see any **CLS** nor **INP** data. That's because both metrics are finalized by the Google Chrome browser when the user navigated away from the page. This is done to be sure that only the occurence with the highest value is being reported. Read about all [nuances of CLS](https://www.rumvision.com/help-center/glossary/metrics/cumulative-layout-shift/?format=md) and [INP characteristics](https://www.rumvision.com/help-center/glossary/metrics/interaction-to-next-paint/?format=md). ## INP breakdown and LoAF entries So, it's mainly additional INP information, including both a breakdown and LoAF entries. In case you're unfamiliar with LoAF: - LoAF stands for [Long Animation Frame](https://www.rumvision.com/blog/long-animation-frames/?format=md); - It's a game changer in debugging long browser tasks that are blocking the main thread; - In short, it's like `git blame` or basically name and shame for third party work, but also 1st party work (like hydration). LoAF was actually officially shipped in Chrome v123 after spending some time in [origin trial phase](https://www.rumvision.com/blog/a-step-in-to-the-future-of-the-web-what-are-origin-trials/?format=md). For a bit of perspective, Chrome v123 was in stable less than a month, meaning that the Google Chrome team is rather quick in including LoAF in their web-vitals.js library. ### Differences between v3 and v4 In web-vitals v3, next to a `loadState` flag we would get `eventEntry`, `eventTarget `and `eventTime` flags when using the `attribution` object. In short, the `event`-prefix has been renamed to `interaction`. But in web-vitals.js v4, you will also get: - `nextPaintTime`, which I think is very convenient to know; - and breakdown information: `inputDelay`, `processingTime` & `presentationDelay`; - `eventEntry` was renamed to `processedEventEntries`, while (as the name is suggesting due to its plural form) `processedEventEntries` is now an array of (potentially multiple) instances of `PerformanceEventTiming's` - and the biggest change: `longAnimationFrameEntries` was added, which again is an array of one or more instances of `PerformanceLongAnimationFrameTiming`, so an actual LoAF. This also is where things can get complicated as one LoAF is then likely to contain multiple `script`'s. Apart from the INP changes mentioned above, LCP's `resourceLoadTime` was renamed to `resourceLoadDuration`. See their [v4 changelog](https://github.com/GoogleChrome/web-vitals/blob/v4/CHANGELOG.md) and for all breaking changes. > Although not INP related, please note that TTFB attribution keys were renamed as well from `*Time` to `*Duration`. See [their upgrading guide](https://github.com/GoogleChrome/web-vitals/blob/v4/docs/upgrading-to-v4.md). ### An INP + LoAF example Let's go over an INP object: ``` { "interactionTarget": "#toggle-nav", "interactionType": "pointer", "interactionTime": 1124.8999999761581, "nextPaintTime": 1332.8999999761581, "processedEventEntries": [ ... { "name": "click", "entryType": "event", "startTime": 1193.7999999821186, "duration": 136, "navigationId": "cfebf9f3-69e7-49aa-9a5b-5ef38e8c43c3", "processingStart": 1217.699999988079, "processingEnd": 1223.699999988079, "cancelable": true } ], "longAnimationFrameEntries": [ { "name": "long-animation-frame", "entryType": "long-animation-frame", "startTime": 1012.0999999940395, "duration": 202, "navigationId": "cfebf9f3-69e7-49aa-9a5b-5ef38e8c43c3", "renderStart": 1072.8999999761581, "styleAndLayoutStart": 1072.8999999761581, "firstUIEventTimestamp": 0, "blockingDuration": 150, "scripts": [ { "name": "script", "entryType": "script", "startTime": 1049, "duration": 10, "navigationId": "cfebf9f3-69e7-49aa-9a5b-5ef38e8c43c3", "invoker": "https://www.rumvision.com/#nav", "invokerType": "classic-script", "windowAttribution": "self", "executionStart": 1057.699999988079, "forcedStyleAndLayoutDuration": 0, "pauseDuration": 0, "sourceURL": "https://www.rumvision.com/#nav", "sourceFunctionName": "", "sourceCharPosition": 0 }, { "name": "script", "entryType": "script", "startTime": 1063.8999999761581, "duration": 6, "navigationId": "cfebf9f3-69e7-49aa-9a5b-5ef38e8c43c3", "invoker": "https://www.rumvision.com/#nav", "invokerType": "classic-script", "windowAttribution": "self", "executionStart": 1070.0999999940395, "forcedStyleAndLayoutDuration": 0, "pauseDuration": 0, "sourceURL": "https://www.rumvision.com/#nav", "sourceFunctionName": "", "sourceCharPosition": 0 } ] }, { "name": "long-animation-frame", "entryType": "long-animation-frame", "startTime": 1215.2999999821186, "duration": 113, "navigationId": "cfebf9f3-69e7-49aa-9a5b-5ef38e8c43c3", "renderStart": 1325.7999999821186, "styleAndLayoutStart": 1326.0999999940395, "firstUIEventTimestamp": 0, "blockingDuration": 53, "scripts": [ { "name": "script", "entryType": "script", "startTime": 1225.5999999940395, "duration": 71, "navigationId": "cfebf9f3-69e7-49aa-9a5b-5ef38e8c43c3", "invoker": "https://www.rumvision.com/file/cdn/ajax/libs/jquery/3.5.1/jquery.min.js", "invokerType": "classic-script", "windowAttribution": "self", "executionStart": 1227.699999988079, "forcedStyleAndLayoutDuration": 0, "pauseDuration": 0, "sourceURL": "https://www.rumvision.com/file/cdn/ajax/libs/jquery/3.5.1/jquery.min.js", "sourceFunctionName": "", "sourceCharPosition": 0 }, { "name": "script", "entryType": "script", "startTime": 1297.8999999761581, "duration": 19, "navigationId": "cfebf9f3-69e7-49aa-9a5b-5ef38e8c43c3", "invoker": "https://www.rumvision.com/file/min/1aaf28b0aec420ebf03598305225278d.js", "invokerType": "classic-script", "windowAttribution": "self", "executionStart": 1299.5999999940395, "forcedStyleAndLayoutDuration": 0, "pauseDuration": 0, "sourceURL": "https://www.rumvision.com/file/min/1aaf28b0aec420ebf03598305225278d.js", "sourceFunctionName": "", "sourceCharPosition": 0 } ] } ], "inputDelay": 90.80000001192093, "processingDuration": 8, "presentationDelay": 109.19999998807907, "loadState": "loading" } ``` This is an example of my very own test in Google Chrome with CPU throttled to 4x slowdown to have a higher chance of running into longer than average tasks and getting interesting data. With a reduced viewport size to trigger the mobile navigation, I then tried to click on the hamburger menu a few times. And we see that there are multiple reported LoAFs, and each LoAF contains multiple scripts. In the example, my interaction happened at 1124ms. But some main thread work was running from 1012ms (`startTime` of the first LoAF) to ( + its `duration`, which is 202ms = ) 1215ms. That's already overlapping my moment of interaction at 1124ms. But right after, another LoAF starts, pushing back next paint (`nextPaintTime` happens at 1332ms) even more. This explains why 2 LoAFs are included in the `longAnimationFrameEntries` object. ## What's next? It's worth mentioning that v4 is still in beta."If you use the code example I provided above you'll notice in your network requests that the resource being embedded by the JavaScript snippet results in a redirect to `https://unpkg.com/web-vitals@4.0.0-beta.0/dist/web-vitals.attribution.iife.js`. ### Bugs & issues The Google team is looking forward to take it out of beta soon! Meanwhile, any bugs, issues etc can be shared over [at their github page](https://github.com/GoogleChrome/web-vitals/issues). ### Changes at RUMvision Regarding RUMvision, there won't be noticeable changes, despite experimenting and implementing v4 already. We did encounter a bug during implementation, which was promptly resolved by the Google Chrome team within a few hours. However, since web-vitals v4 is still in beta, it's not the default version that we include with our RUM script. RUMvision was an [early adopter](https://www.rumvision.com/blog/how-rumvision-leads-the-way-in-real-user-monitoring/?format=md) of the LoAF API. This means we're already exposing INP and [LoAF breakdown in our features](https://www.rumvision.com/help-center/filters/metric/loaf/?format=md) and dashboards. As a result and because of a generally smooth and backwards compatible implementation nothing will change from a RUMvision user perspective.