JavaScript Navigator sendBeacon() Method

Beginner
⏱️ 10 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
✅ Baseline Widely available

What You’ll Learn

navigator.sendBeacon() sends a small async HTTP POST for analytics without blocking the next page. Learn the boolean return value, visibilitychange best practice, the ~64 KiB limit, five examples, and try-it labs.

01

Kind

Method

02

Returns

boolean

03

Status

Baseline Widely available

04

HTTP

POST only

05

Size limit

~64 KiB queued

06

Best trigger

visibilitychange

Introduction

Analytics often need to fire when the user leaves a page. Old tricks used synchronous XMLHttpRequest or delayed unload with image beacons — and made the next page feel slow.

sendBeacon(url, data) queues a small POST that the browser can finish in the background. Navigation stays snappy, and you still get a reliable chance to log the event.

💡
No Dep / Exp / Non-standard banner

MDN marks Baseline Widely available (since April 2018). Prefer visibilitychange over unload / beforeunload.

Understanding the sendBeacon() Method

  • URL required — relative or absolute endpoint that accepts POST.
  • Data optional — string, Blob, FormData, URLSearchParams, buffers, etc.
  • Returns booleantrue if queued, false if not.
  • No response body — you cannot read the server reply (use fetch if you need it).
  • ~64 KiB limit — keep payloads small.
  • Does not block unload — designed for end-of-session analytics.

📝 Syntax

General forms of the method:

JavaScript
navigator.sendBeacon(url)
navigator.sendBeacon(url, data)

Parameters

  • url — string URL that will receive the POST.
  • data (optional) — body payload (string, Blob, FormData, URLSearchParams, ArrayBuffer, TypedArray, or DataView).

Return value

  • true if the data was successfully queued; otherwise false.

MDN-style end-of-session pattern

JavaScript
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    navigator.sendBeacon("/log", analyticsData);
  }
});

⚡ Quick Reference

GoalCode
Feature detecttypeof navigator.sendBeacon === "function"
Send stringnavigator.sendBeacon("/log", "ping")
Send JSON Blobnavigator.sendBeacon(url, new Blob([json], { type: "application/json" }))
On page hidevisibilitychange + visibilityState === "hidden"
Check queue resultconst ok = navigator.sendBeacon(url, data)
Need response / headersfetch(url, { method: "POST", keepalive: true, … })

🔍 At a Glance

Four facts to remember about navigator.sendBeacon().

Returns
boolean

Queued?

Status
Baseline

Widely available

Method
POST

Beacon API

Limit
~64KiB

Keep it small

📋 sendBeacon vs fetch keepalive

sendBeacon()fetch(..., { keepalive: true })
HTTP methodPOST onlyAny method you set
Custom headersLimited / not the goalFull control
Read responseNoYes (when the request completes)
Best forSimple analytics pingsRicher unload requests

Examples Gallery

Examples queue beacons to demo endpoints. In Try It Yourself, use a path on the same origin or a public test URL — the important part is the boolean queue result and the visibilitychange pattern.

📚 Getting Started

Detect the API and queue a simple beacon.

Example 1 — Feature Detection

Confirm sendBeacon exists before relying on it.

JavaScript
const supported = typeof navigator.sendBeacon === "function";
console.log(supported ? "sendBeacon available" : "sendBeacon missing");
Try It Yourself

How It Works

On very old browsers, fall back to fetch with keepalive or a tiny pixel request.

Example 2 — Queue a String Beacon

Send a small text payload and log whether it was queued.

JavaScript
function pingAnalytics() {
  if (typeof navigator.sendBeacon !== "function") {
    return "sendBeacon not supported";
  }
  // Demo endpoint — replace with your analytics URL in production
  const queued = navigator.sendBeacon("/analytics", "event=page_ping");
  return "queued: " + queued;
}

console.log(pingAnalytics());
Try It Yourself

How It Works

true only means “accepted into the queue” — not that the server already replied 200.

📈 Practical Patterns

Fire on hide, send JSON as a Blob, and wrap safely.

Example 3 — visibilitychange (MDN)

Most reliable beginner pattern for end-of-session analytics.

JavaScript
const analyticsData = "session=end&ts=" + Date.now();

document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    const ok = navigator.sendBeacon("/log", analyticsData);
    console.log("beacon on hide queued:", ok);
  }
});

console.log("Listening for visibilitychange (switch tab to test)");
Try It Yourself

How It Works

Avoid relying on unload / beforeunload — they are unreliable and hurt bfcache.

Example 4 — JSON Blob Payload

Send structured analytics with the correct content type via Blob.

JavaScript
function sendJsonBeacon(url, payload) {
  const body = new Blob([JSON.stringify(payload)], {
    type: "application/json"
  });
  return navigator.sendBeacon(url, body);
}

const ok = sendJsonBeacon("/analytics", {
  event: "click",
  path: location.pathname,
  t: Date.now()
});
console.log("json beacon queued: " + ok);
Try It Yourself

How It Works

Keep the JSON small so you stay under the ~64 KiB queue limit.

Example 5 — Safe Helper + Size Guard

Feature-detect, reject oversized payloads early, and fall back to fetch keepalive.

JavaScript
const MAX_BEACON = 64 * 1024;

function sendAnalytics(url, data) {
  const asString = typeof data === "string" ? data : String(data);
  if (asString.length > MAX_BEACON) {
    return { ok: false, reason: "too-large" };
  }
  if (typeof navigator.sendBeacon === "function") {
    const queued = navigator.sendBeacon(url, asString);
    return { ok: queued, reason: queued ? "beacon" : "not-queued" };
  }
  // Fallback sketch for environments without sendBeacon
  try {
    fetch(url, { method: "POST", body: asString, keepalive: true });
    return { ok: true, reason: "fetch-keepalive" };
  } catch (err) {
    return { ok: false, reason: err.name };
  }
}

console.log(JSON.stringify(sendAnalytics("/analytics", "ok")));
console.log(JSON.stringify(sendAnalytics("/analytics", "x".repeat(70000))));
Try It Yourself

How It Works

Pre-checking size avoids silent false returns from oversized queue attempts.

🚀 Common Use Cases

  • Session analytics — log duration / last page when the tab hides.
  • Error / diagnostics pings — ship a tiny crash summary without blocking unload.
  • Feature usage — count button clicks that happen right before navigation.
  • A/B metrics — record experiment assignment at leave time.
  • Performance breadcrumbs — send a compact timing snapshot on hide.

🧠 How navigator.sendBeacon() Works

1

Prepare a small payload

Keep analytics under ~64 KiB.

Data
2

Call sendBeacon

Pass URL + optional body; get a boolean queue result.

Queue
3

Browser sends POST later

Asynchronously, without delaying the next page.

POST
4

Server logs the event

Your analytics endpoint records the beacon body.

📝 Notes

  • Baseline Widely available (MDN, since April 2018).
  • Not Deprecated, Experimental, or Non-standard — no status banner required.
  • Prefer visibilitychange; avoid unload / beforeunload for analytics.
  • ~64 KiB queue limit; POST only; no response access.
  • Related: setAppBadge(), canShare(), requestMIDIAccess(), Window.

Universal Browser Support

navigator.sendBeacon() is Baseline Widely available across modern browsers (MDN: since April 2018). Use it for small analytics POSTs, especially on visibilitychange. For larger payloads or custom methods/headers, prefer fetch with keepalive: true.

Baseline · Widely available

Navigator.sendBeacon()

Async analytics POST that does not block the next navigation.

Universal Widely available
Google Chrome Beacon API · modern versions
Full support
Mozilla Firefox Beacon API · modern versions
Full support
Microsoft Edge Beacon API · Chromium
Full support
Apple Safari Beacon API · modern versions
Full support
Opera Beacon API · modern versions
Full support
Internet Explorer No modern sendBeacon — use a fallback
Unavailable
sendBeacon() Excellent

Bottom line: Detect the method, keep payloads small, fire on visibility hidden, check the boolean queue result, and fall back to fetch keepalive when needed.

Conclusion

navigator.sendBeacon() is the Baseline way to ship tiny analytics POSTs without slowing the next page. Trigger it on visibilitychange, keep payloads small, and treat the return value as “queued,” not “server confirmed.”

Continue with setAppBadge(), canShare(), or the JavaScript hub.

💡 Best Practices

✅ Do

  • Use visibilitychange when the page becomes hidden
  • Keep payloads under ~64 KiB
  • Check the boolean return value
  • Send only the fields you need
  • Fall back to fetch + keepalive when needed

❌ Don’t

  • Rely on unload / beforeunload for analytics
  • Expect to read a response body
  • Ship huge JSON dumps as beacons
  • Assume true means HTTP 200 already happened
  • Block navigation with sync XHR workarounds

Key Takeaways

Knowledge Unlocked

Five things to remember about sendBeacon()

Baseline analytics POST — queue small data without blocking navigation.

5
Core concepts
02

Status

Baseline

Widely available
👁 03

Trigger

visibilitychange

hidden
📏 04

Limit

~64 KiB

Small payloads
📈 05

Returns

queued?

boolean

❓ Frequently Asked Questions

It queues a small HTTP POST request to a URL, typically for analytics. The browser sends it asynchronously without blocking page unload or the next navigation.
No. MDN marks it Baseline Widely available (since April 2018). It is not Deprecated, Experimental, or Non-standard — no status banner is required.
A common pattern is on visibilitychange when document.visibilityState becomes "hidden" — more reliable than unload or beforeunload, especially on mobile.
true means the user agent successfully queued the data for transfer. false means it could not queue it (for example the payload is too large or the browser refused).
Queued data is limited to about 64 KiB (65,536 bytes). For larger transfers or custom methods/headers/response access, use fetch() with keepalive: true.
Optional data can be a string, Blob, FormData, URLSearchParams, ArrayBuffer, TypedArray, or DataView.
Did you know?

Pages that register unload handlers can be excluded from the browser’s back/forward cache. That is one more reason MDN recommends visibilitychange + sendBeacon() for leave-time analytics.

Explore setAppBadge() Next

Show unread counts on installed PWA icons with the Badging API.

setAppBadge() →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

8 people found this page helpful