Documentation / Google Web Vitals

Google Web Vitals

Using #

If you use Chrome/Edge Google Web Vital metrics is collected automatically.

First Contentful Paint (FCP) #

To see when the first contentful paint happens you should record a video and use visual metrics --video --visualMetrics. Then you can go to the filmstrip or video view to see when the first content is painted on the screen.

First contentful paint

To collect the metric first contentful paint we use the Paint Timing API.

Largest Contentful Paint (LCP) #

Go the the metrics tab and scroll down to the Largest Contentful Paint metrics and you will see a screenshot where the element that is the largest is highlighted in red.

Largest contentful paint

The screenshot is generated after the page finished loading. If the largest contentful element has been removed from the screen at that time, you will not see any highlight in the screenshot. You can then instead use the information in the table to the left to identify the element.

If you have a hard time identify the element on the screenshot you can change the color of the highlighting by --browsertime.screenshotLCPColor blue.

Cumulative Layout Shift (CLS) #

The layout shift API helps you find the DOM elements that shifts on the screen that degrades the user experience.

If those elements are still in the viewport after the page finished loading we try to highlight them in a screenshot. By default all elements that has a shift value of 0.01 or higher is highlighted. You can change that with --browsertime.screenshotLSLimit. That can help you if you have a lot of elements that shifts. Say that you want to highlight only elements with a value higher than 0.1 then add --browsertime.screenshotLSLimit 0.1 to your run settings.

Layout Shift

You can also change the color of the highlight: --browsertime.screenshotLSColor blue

Remember that the API points out the element that is shifted, not the element that actually pushed the the other element.

Total Blocking Time (TBT) / First input delay #

Total blocking time is harder: It really depends on what CPU you use when you run your tests (test on real mobile phones!). Total blocking time use the Long Tasks API to get long running tasks. The API have very limited support to show what causes the long tasks.

The best way to get valuable information is to use --cpu to get the Chrome trace log to download and drag and drop into your performance tab of devtools in Chrome. If you need a deeper trace log (with more information) you can add extra trace categories to the tracelog. The CPU profiler do that: disabled-by-default-v8.cpu_profiler.

Calibrating metrics against CrUX 75 percentile #

Using the Chrome User Experience plugin you can get the metrics of what your user is experience and you can use those values to try to calibrate the metrics you get out of

It can be hard though since: In the real world people use a lot of different devices with different CPU, many many different connectivities and so on. The easiest thing to calibrate is to have the same first contentful paint in your test as in your Chrome user experience data. Do that by increasing/decreasing the connectivity until you have something like the same values.

Budget #

You can easily run your budget tests against Google Web Vitals. First create a budget configuration file.

  "budget": {
    "googleWebVitals": {
      "firstContentfulPaint": 500,
      "largestContentfulPaint": 1200,
      "totalBlockingTime": 200,
      "cumulativeLayoutShift": 0

And then run:

docker run --rm -v "$(pwd):/" sitespeedio/ --budget.configPath budget.json

Metrics in Graphite and Grafana #

Our pre-made dashboards includes Google Web Vitals where you can see latest metrics and trends compared to last week.

Google Web Vitals trends

Bug reports #

If you don’t get the correct metrics it could either be a bug in the browser API or in

To verify and check that the metrics seems to be correct, you can load your page in Chrome and then copy/paste the following snippets in the console and look at the console log. That is useful if you want to file a bug for Chrome.

To get the first contentful paint:

(function() {
    const entries = window.performance.getEntriesByType('paint');
    for (const entry of entries) {

To get the largest contentful paint element:

(function() {
const observer = new PerformanceObserver(list => {});
  observer.observe({ type: 'largest-contentful-paint', buffered: true });
  const entries = observer.takeRecords();
  if (entries.length > 0) {
    const largestEntry = entries[entries.length - 1];

To get the layout shift information you can run:

(function() {
const observer = new PerformanceObserver(list => {});
  observer.observe({ type: 'layout-shift', buffered: true });
  const entries = observer.takeRecords();
   for (let entry of entries) {
    if (entry.hadRecentInput) {

And to get the long tasks you can use:

(function() {
const observer = new PerformanceObserver(list => {});
  observer.observe({ type: 'longtask', buffered: true });
  const entries = observer.takeRecords();
   for (let entry of entries) {

If you suspect the bug to be in please file a issue in

Using CruX # comes with a Chrome User Experience plugin. That makes it easy to get the metrics that Google collects from your users. You can compare them with the ones you get from

Sending the CrUX data to Graphite you can see metrics both per URL and per origin.


Go to the CrUX documentation on how to set it up.

Using Lighthouse #

Use the Lighthouse plugin to run Lighthouse from and collect Google Web Vitals.