JavaScript Beacon API and navigator.sendBeacon Guide

This article provides an overview of the JavaScript Beacon API and explains how the navigator.sendBeacon() method reliably sends diagnostic and analytics data to a web server during page unload. You will learn about the limitations of traditional asynchronous requests during document termination, how the Beacon API solves these problems by offloading network transmission to the browser background process, and the proper syntax and best practices for implementing it in your applications.

The Problem with Traditional Requests During Page Unload

Historically, developers relied on standard XMLHttpRequest or the fetch() API inside unload or beforeunload event listeners to send analytics, metrics, or session data when a user navigated away from a webpage.

This approach has major flaws: - Dropped Requests: Asynchronous fetch or XMLHttpRequest calls are often canceled immediately because the browser terminates the execution context when the document unloads. - Degraded Performance: To force data transmission, developers previously used synchronous XMLHttpRequest calls. This blocks the main thread, delays the loading of the next page, and creates a sluggish user experience. Consequently, modern browsers have deprecated synchronous requests during unload events.

What is the Beacon API?

The Beacon API was introduced by the W3C to solve the challenge of sending telemetry and state data at the end of a user session. It exposes a single method, navigator.sendBeacon(), which schedules an asynchronous, non-blocking HTTP POST request managed directly by the browser rather than the document’s script execution environment.

Because the browser’s networking layer handles the request independently of the document’s lifecycle, the data is guaranteed to be transmitted even after the page has completely closed, without delaying the user’s navigation.

How navigator.sendBeacon() Works

When navigator.sendBeacon() is invoked, the browser queues the request in an internal buffer. The method immediately returns a boolean value: - true if the browser successfully accepted and queued the request for delivery. - false if the request could not be queued (e.g., if the payload exceeds size limits).

The browser then transmits the data over HTTP POST in the background when it is optimal to do so, requiring no callbacks or response handling.

Syntax and Parameters

navigator.sendBeacon(url, data);

Practical Code Example

function sendAnalyticsData() {
  const url = "/api/analytics";
  const payload = JSON.stringify({
    event: "session_end",
    timestamp: Date.now(),
    timeSpentOnPage: performance.now()
  });

  const blob = new Blob([payload], { type: "application/json" });

  if (navigator.sendBeacon) {
    navigator.sendBeacon(url, blob);
  } else {
    // Fallback using fetch with keepalive
    fetch(url, { method: "POST", body: payload, keepalive: true });
  }
}

// Recommended lifecycle event for modern browsers
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    sendAnalyticsData();
  }
});

Best Practices and Limitations