Real user monitoring: measure your own visitors

Real user monitoring records the speed your visitors really get. What it adds to lab tests and CrUX, how the web-vitals library works, and how to use the data.

AdvancedHow-to6 min readUpdated October 30, 2026

In this guide
  1. What real user monitoring adds
  2. The web-vitals library
    1. What to know before you trust the numbers
    2. The navigation type
  3. Find the cause with attribution
  4. Make the data useful
  5. Privacy and weight
  6. Mistakes to avoid
  7. Check your own page
  8. Sources

What real user monitoring adds

You already have two kinds of data. A lab test such as Lighthouse loads a page once, under fixed conditions, and gives you a repeatable result with a list of causes. The Chrome UX Report gives you real-visitor data for the sites with enough traffic, collected from Chrome users, over a 28-day window. See lab and field data and the Chrome UX Report.

RUM is your own collection of field data. It adds:

It also costs something. A script runs on your pages, it collects data you must store and read, and it has limits. It supplements the other tools. It does not replace them.

The web-vitals library

The Chrome team's web-vitals library is a small, modular library for measuring the Web Vitals on real users. Its documentation says it measures them in a way that accurately matches how Chrome measures them and how they are reported to other Google tools, such as the Chrome UX Report, PageSpeed Insights and Search Console. It measures:

The core is about 3 KB compressed with Brotli. Install it from npm or load it from a CDN. Each function takes a callback that is called when the value is ready. This example sends each result to your own endpoint:

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

function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
    id: metric.id,
    navigationType: metric.navigationType,
    page: location.pathname,
  });
  // sendBeacon works while the page is closing
  navigator.sendBeacon('/analytics', body);
}

onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);

This follows the library's own example, with a few extra labels. The /analytics address is yours to provide. It can be a function on your server, a worker, or an analytics service that accepts events.

What to know before you trust the numbers

The documentation lists several behaviors:

The navigation type

Each metric carries a navigationType. It is the value from the Navigation Timing interface, with some additions: back-forward-cache for a page restored from the back/forward cache, prerender for a page that was prerendered, restore for a page the browser discarded and the visitor restored, and soft-navigation for soft navigations. Keep it as a label. A restored page will have a very fast value, and mixing it in without a label will make the average look better than a fresh load. See speculation rules.

Find the cause with attribution

The library has an attribution build, which adds data to each report to help you diagnose why a value is what it is. For LCP it names the element. For CLS, the element that shifted most. For INP, the element involved in the slowest interaction and a breakdown into input delay, processing time and presentation delay. Send these along with the number. The README example sends them as extra parameters to Google Analytics:

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

onINP(({name, value, attribution}) => {
  navigator.sendBeacon('/analytics', JSON.stringify({
    name,
    value,
    element: attribution.interactionTarget,
  }));
});

The README says the attribution build is slightly larger than the standard one, by about 1.5 KB compressed, so use it only if you will use the extra data. Its INP data also includes a load-state value, which tells you whether the page was still loading when the slow interaction happened. If slow interactions happen while the page is loading, the cause is start-up work. See how to fix INP.

Make the data useful

  1. Choose the labels first. Page template, device class, country, navigation type. Without labels, a single average hides the pages that fail.
  2. Read a percentile, not an average. The Core Web Vitals use the 75th percentile of visits. Group your data by page type and look at the 75th percentile of each metric. The library gives thresholds: LCP 2,500 and 4,000 milliseconds, INP 200 and 500 milliseconds, CLS 0.1 and 0.25. Each report also carries a rating of good, needs improvement or poor, so you do not have to compute it.
  3. Watch the trend, not single days. Visitors, devices and campaigns change from day to day.
  4. Compare with CrUX. If your numbers differ a lot from the Chrome UX Report's, check for iframes, for browsers CrUX does not cover, and for how you sample.
  5. Tie changes to releases. Note the date of each deploy, plugin change and new script, and look at the metrics around it.

Privacy and weight

Mistakes to avoid

Check your own page

Run the free audit for a lab result, then compare it with the real-visitor numbers for the same page in PageSpeed Insights. If you do not have enough traffic for the Chrome UX Report, or you need page-level answers, add the web-vitals library and collect your own.

Sources

Start here

Free website audit

Live Google data for any public page: mobile and desktop.

Mobile + desktop · Lighthouse lab test · Real-user Chrome data when Google has it

Rather have this done for you? Core Web Vitals · Book a call

Related guides

All guides →