Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
PerformanceJune 26, 20265 min read

Core Web Vitals Tracking: Implementing Reliable RUM with Beacon API

Core Web Vitals tracking requires more than synthetic tests. Learn to build robust performance observability using the Beacon API for accurate RUM data.

PerformanceWeb VitalsBeacon APIFrontend EngineeringObservabilityFrontend

We’ve all been there: the synthetic dashboard shows green, but the Slack channel is blowing up with users complaining about a "sluggish" site. Relying on lab data is like checking the weather report while sitting in a windowless bunker; it tells you what the environment should be, not what your users are actually experiencing.

To fix this, I recently overhauled our RUM (Real User Monitoring) strategy. We needed to move away from unreliable XMLHttpRequest pings—which often get canceled when a user navigates away—and move toward a more robust, user-centric approach. Here is how we implemented a production-ready observability pipeline using the Beacon API and standard Web Vitals.

Why the Beacon API is non-negotiable for RUM

When you track metrics like Largest Contentful Paint (LCP) or Interaction to Next Paint (INP), you’re often sending data at the exact moment a user is leaving the page or clicking a link. If you use a standard fetch or XHR request, the browser will likely terminate those requests as the document unloads.

The Beacon API, specifically navigator.sendBeacon(), was designed for this. It queues an asynchronous, non-blocking POST request that the browser guarantees to attempt even if the page is being closed. It’s fire-and-forget, exactly what you need for telemetry.

JAVASCRIPT
// A simple wrapper to report metrics
function reportMetric(metric) {
  const data = JSON.stringify({
    name: metric.name,
    value: metric.value,
    id: metric.id,
  });

  // The browser ensures this is sent even if the user closes the tab
  navigator.sendBeacon(CE9178">'/analytics/performance', data);
}

Architecting Performance Observability

When we first set up our telemetry, we made the mistake of sending everything. Every mouse move, every scroll event, every tiny layout shift. Our database choked, and our observability costs skyrocketed. We learned the hard way that you need to sample your data and prioritize your metrics.

To bridge the gap between client-side issues and server-side performance, we now use Performance observability: Bridging Backend Bottlenecks and Core Web Vitals to ensure we aren't just looking at frontend numbers in a vacuum. If a user reports a high LCP, I want to know if it was a slow network or a slow server response.

When you're diving into these metrics, don't forget that Core Web Vitals Optimization: Connecting Backend Latency to RUM is key to understanding the full lifecycle of a request. By injecting Server-Timing headers, you can correlate your backend processing time with the metrics captured by the Beacon API.

Implementing the Observer Pattern

Don't reinvent the wheel. Use the web-vitals library. It’s the industry standard for a reason. Instead of writing custom event listeners for layout-shift (which is a headache), import the library and pipe it into your beacon handler.

JAVASCRIPT
import { onLCP, onFID, onCLS } from CE9178">'web-vitals';

function sendToAnalytics(metric) {
  const body = JSON.stringify(metric);
  // Use navigator.sendBeacon for reliable delivery
  navigator.sendBeacon(CE9178">'/api/v1/metrics', body);
}

onLCP(sendToAnalytics);
onFID(sendToAnalytics);
onCLS(sendToAnalytics);

One caveat: onCLS can report multiple times as the page layout stabilizes. You might want to buffer these or just send the final value to avoid bloating your logs with intermediate states. We found that sending just the final delta is usually enough for most reporting tools.

The "Wrong Turn" We Took

Initially, we tried to build a custom buffer that would batch all metrics into a single request every 5 seconds. It worked fine until we ran into mobile devices with aggressive background-process throttling. The batching logic would often fail to execute because the main thread was too busy during navigation.

We switched to a "send-on-event" model using the Beacon API. While it creates more individual requests, the overhead is negligible compared to the reliability of the data we get back. It’s roughly 1.8x more data points than we had before, but the accuracy is finally high enough to trust for decision-making.

Integrating Server-Side Context

Tracking Core Web Vitals is only half the battle. If your LCP is consistently poor in a specific region, you need to know if it’s an edge-caching issue or a database bottleneck. By combining the Beacon API with the Server-Timing API for INP Optimization: Debugging Backend Latency, you can start to see the full picture.

I’m still not entirely sold on our current sampling rate of 10%. Sometimes, rare performance issues only show up in the bottom 5% of traffic, and we might be missing them. Next quarter, I’m planning to implement a "high-fidelity" sampling mode for users who report slow interactions, triggered by a small PerformanceObserver threshold.

FAQ

Q: Does the Beacon API support custom headers? A: No, navigator.sendBeacon is limited. You cannot set custom headers like Authorization or Content-Type. You have to handle authentication via cookies or tokens in the URL query parameters.

Q: Should I use Beacon API for critical business data? A: Absolutely not. It is fire-and-forget. If the request fails, you won't know, and you won't get a retry. Keep it strictly for telemetry and observability.

Q: How does this affect my site's performance? A: Because Beacon requests are asynchronous and handled by the browser's networking stack outside the main thread, they have virtually zero impact on your users' experience.

Final thoughts

Building a robust performance observability pipeline is less about the tools and more about the discipline of data collection. Start small, use the standard web-vitals library, and rely on the Beacon API for your transport layer. You'll stop guessing about why your site feels slow and start having the data to prove exactly where the bottlenecks live.

Back to Blog

Similar Posts

PerformanceJune 28, 20264 min read

Core Web Vitals Optimization: Eliminating Critical Request Chains

Learn to eliminate critical request chains and boost Core Web Vitals. Discover how precise resource prioritization and preload scanning improve load times.

Read more
PerformanceJune 27, 20264 min read

Server-Side Rendering Resilience: Fixing Hydration and Layout Shifts

Master Server-Side Rendering resilience by preventing hydration mismatches and layout shifts. Learn how streaming and atomic updates stabilize Core Web Vitals.

Read more
PerformanceJune 27, 20265 min read

Font Loading Strategy for Core Web Vitals and Better UX

Master your Font Loading Strategy to improve Core Web Vitals. Learn how to prevent FOIT and Cumulative Layout Shift using variable fonts and CSS best practices.

Read more