Real User Monitoring (RUM) Guide

February 13, 2026 | Monitoring RUM Performance

Core Web Vitals and implementation.

Real User Monitoring (RUM): Measuring What Users Experience

Server-side metrics tell you how your backend performs, but they miss the complete picture. Real User Monitoring captures actual browser performance data — page load times, JavaScript errors, and Core Web Vitals — from real users in real conditions.

What RUM Captures

  • Navigation Timing — DNS lookup, TCP connection, TLS handshake, server response, DOM parsing, page load
  • Core Web Vitals — LCP (Largest Contentful Paint), FID/INP (Interaction to Next Paint), CLS (Cumulative Layout Shift)
  • JavaScript Errors — Unhandled exceptions, promise rejections, console errors
  • AJAX/Fetch Calls — XHR and fetch request timing, error rates, payloads
  • User Journey — Page navigation flows, conversion funnels, abandonment points

Core Web Vitals Explained

MetricGoodNeeds WorkPoorWhat It Measures
LCP≤ 2.5s≤ 4.0s> 4.0sVisual loading speed
INP≤ 200ms≤ 500ms> 500msInteractivity responsiveness
CLS≤ 0.1≤ 0.25> 0.25Visual stability

Implementation with web-vitals Library

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

function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
    delta: metric.delta,
    id: metric.id,
    page: window.location.pathname,
    userAgent: navigator.userAgent
  });

  // Use sendBeacon for reliable delivery
  navigator.sendBeacon('/api/vitals', body);
}

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

Server-Side Collection Endpoint

[ApiController]
[Route("api/vitals")]
public class VitalsController : ControllerBase
{
    [HttpPost]
    public IActionResult Collect([FromBody] WebVital vital)
    {
        // Log to your monitoring system
        _logger.LogInformation(
            "WebVital: {Name}={Value} rating={Rating} page={Page}",
            vital.Name, vital.Value, vital.Rating, vital.Page);

        // Forward to Prometheus/Datadog/CloudWatch
        _metrics.RecordHistogram($"web_vital_{vital.Name}", vital.Value,
            new[] { "page", vital.Page });

        return Ok();
    }
}

Grafana Dashboard for Core Web Vitals

# LCP P75 by page
histogram_quantile(0.75, sum(rate(web_vital_LCP_bucket[1h])) by (le, page))

# Percentage of pages with "Good" LCP
sum(web_vital_LCP_bucket{le="2500"}) / sum(web_vital_LCP_count) * 100

# CLS distribution
histogram_quantile(0.75, sum(rate(web_vital_CLS_bucket[1h])) by (le, page))

Common Performance Issues

  • High LCP — Unoptimized hero images, render-blocking CSS/JS, slow server response
  • High INP — Heavy JavaScript execution, long tasks blocking the main thread
  • High CLS — Images without dimensions, dynamically injected content, web fonts causing FOUT

RUM vs Synthetic Monitoring

  • RUM — Real user data, diverse devices/networks, shows actual experience. But only captures data when users visit.
  • Synthetic — Controlled tests, consistent baselines, detects issues before users see them. But doesn't reflect real user conditions.
  • Best practice: Use both. Synthetic for regression detection, RUM for real-world impact assessment.

Eazy SaaS Tip: We implement Core Web Vitals monitoring for every client's production site. Google uses these metrics for search ranking, so improving them directly impacts SEO performance. We typically achieve "Good" ratings for 90%+ of page views within one optimization sprint.