On this page
A fast laboratory test cannot tell you whether customers on older phones experience slow interactions after ten minutes of use. Real-user monitoring, or RUM, measures performance during actual visits. The goal is not another dashboard: it is a reliable feedback loop connecting user experience, releases and the work your team chooses next.
Define the field questions before adding scripts
Start with specific questions: which page templates miss performance targets, whether a release changed mobile responsiveness, and whether slow experiences cluster around a region or navigation type. Record a small set of dimensions that can answer those questions. Avoid collecting every available browser attribute simply because it exists.
The API latency tester can help check your collection endpoint's response behaviour before rollout. Send an appropriate test request to an endpoint you control and verify status and timing. That check does not measure Core Web Vitals; it helps ensure your telemetry transport does not become an unreliable dependency.
Distinguish field data from controlled experiments. Laboratory tools reproduce conditions and explain mechanisms; RUM reveals what your audience actually encounters. Use both. A field regression can send you back to a trace, while a lab improvement needs field confirmation before you claim that customers benefited.
Choose an owner for the collection pipeline and an owner for each important page family. Without that agreement, low scores tend to circulate between frontend, hosting and analytics teams without an investigation. Keep the instrumentation plan with your performance and SEO resources so the measurements remain understandable after staff changes.
Know what LCP, INP and CLS represent
Largest Contentful Paint, or LCP, describes when the largest eligible visible content element is rendered during loading. A slow value can reflect server delay, resource discovery, transfer time or rendering delay. It does not by itself identify which phase is responsible. Inspect candidate elements and timing details before deciding to compress an image.
Interaction to Next Paint, or INP, summarizes responsiveness across qualifying interactions during a visit. Slow handlers, long unrelated tasks and expensive rendering can all contribute. A page without qualifying interactions may not produce an INP value, so missing INP does not mean zero latency or an exceptionally responsive experience.
Cumulative Layout Shift, or CLS, measures unexpected visual instability using a session-window model. It is unitless, unlike LCP and INP, which are measured in milliseconds. Content injected long after page load can matter, making short synthetic runs insufficient for some layout problems. The Core Web Vitals overview explains the metrics and evaluation model.
The good thresholds are LCP at or below 2,500 milliseconds, INP at or below 200 milliseconds and CLS at or below 0.1. Assess each at the 75th percentile of the relevant field distribution, commonly separating mobile and desktop. These thresholds are not permission to ignore slower users or to average all three metrics into one invented score.
Instrument with a small, explicit event schema
Install a reviewed version of web-vitals through your package manager and load it early enough to observe the visit. The library handles important lifecycle details that a handful of raw PerformanceObserver callbacks can miss. Its official repository documents supported browsers, reporting options and attribution builds.
The following example sends compact events to a same-origin endpoint. It assumes your application supplies a release identifier and a normalized route template without query strings or user identifiers. Apply your consent policy before initializing collection, and independently decide whether the entire page view belongs in your sample.
import { onCLS, onINP, onLCP } from "web-vitals";
const release = "web-2026-11-a";
const route = document.documentElement.dataset.routeTemplate || "unknown";
function report(metric) {
const payload = JSON.stringify({
schemaVersion: 1,
name: metric.name,
value: metric.value,
rating: metric.rating,
metricId: metric.id,
navigationType: metric.navigationType,
route,
release,
});
const body = new Blob([payload], { type: "application/json" });
if (!navigator.sendBeacon("/rum", body)) {
void fetch("/rum", {
method: "POST",
body,
keepalive: true,
credentials: "same-origin",
}).catch(() => {});
}
}
onLCP(report);
onINP(report);
onCLS(report);Beacon delivery is best effort: an accepted queue request is not proof that the server stored an event. Keep payloads small and never block navigation while waiting for analytics. The MDN sendBeacon reference explains its intended use and limitations.
If you enable reportAllChanges, several updates can share a metric identifier. Upsert the latest value for that identifier instead of counting each update as another visit. Do not sum metric.value across reports. Define identifier scope and retention on the server, validate numeric ranges, and rate-limit ingestion so malformed traffic does not poison your distributions.
Aggregate carefully and preserve useful context
Calculate percentiles from comparable observations, not by averaging daily percentiles. An average of page-template percentiles is also not the site-wide percentile. If you cannot retain raw observations, use a mergeable distribution structure with documented precision. Always show sample counts beside the result.
Apply page-view sampling consistently across metrics so the dataset does not disproportionately include only slow events or only visitors who interact. A random decision once per eligible visit is a practical starting point. Preserve the sampling rate and monitor changes in collection coverage, because a sudden score improvement can actually be a broken instrumentation deployment.
Normalize routes such as product detail pages to a stable template. Raw paths and query strings can leak sensitive information and create thousands of tiny groups. Add only dimensions that support decisions, such as release, broad device class and page family. Small segments are noisy and can also undermine privacy.
Compare your results with Chrome UX Report where available, but expect differences in audience, browser support, sampling and aggregation windows. Your RUM does not automatically reproduce Google's dataset. Single-page application transitions also need explicit interpretation: assigning an ordinary document-lifecycle metric to the current route does not magically make it a valid soft-navigation metric.
Turn regressions into targeted investigations
Alert on sustained changes with sufficient samples, not every small percentile movement. Compare equivalent days and audiences, then annotate deployments and experiments. A regression concentrated in one release and template is easier to investigate than a site-wide red badge with no supporting context.
For slow LCP, inspect discovery and server timing before changing image formats. For poor INP, reproduce the relevant interaction and inspect long tasks. For elevated CLS, examine late content insertion and reserved dimensions using the layout-shift debugging guide. Bring attribution data into the pipeline only when you have a clear diagnostic use and privacy review.
Treat fixes as hypotheses. A reduction in third-party work or JavaScript payload size may improve responsiveness, but verify the affected cohort after release. Keep a rollback path and record what changed. The useful output of RUM is an evidence-backed engineering decision, not an endlessly refreshed leaderboard.
Key takeaways
- Combine real-user distributions with controlled diagnostic testing.
- Collect LCP, INP and CLS with clear units and lifecycle semantics.
- Normalize routes, minimize personal data and sample consistently.
- Deduplicate metric updates and display percentiles with sample counts.
- Connect regressions to releases, investigate causes and verify fixes in the field.
Reach designers & developers at work
Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.
Become a sponsor