How to Detect Page Visibility and Safely Handle Unload Events in Modern Web Apps

Understanding when users leave or hide a page is crucial for UX, analytics, and performance; this guide explains the Page Visibility API, beforeunload/unload events, the sendBeacon method, and pagehide/pageshow handling, offering code examples and best‑practice recommendations for reliable detection and data reporting.

JavaScript
JavaScript
JavaScript
How to Detect Page Visibility and Safely Handle Unload Events in Modern Web Apps

In modern web development, we often need to know whether the user is still staying on the current page. This seemingly simple requirement is linked to user experience, data analysis, and system performance.

Switch to another browser tab or application (page becomes hidden but not closed).

Minimize the browser window (same as above).

Close the browser tab or the entire browser .

Navigate to a new URL in the current tab .

Switch to another app or return to the home screen on mobile devices .

For these different scenarios, the front‑end provides various techniques and APIs to make the determination.

Method 1: Page Visibility API – Modern Preferred

This is the standard way to handle “whether the page is visible to the user”. It is specifically designed to detect if a page is hidden or shown, making it ideal for handling tab switches, window minimization, and similar cases.

Core concepts:

document.hidden : a read‑only property that returns true when the page is in the background or minimized, otherwise false.

visibilitychange event : fires on the document object whenever the value of document.hidden changes.

Applicable scenarios:

Pause/play video or audio.

Stop/start animations or carousels.

Pause server polling and resume when the page becomes visible again.

Code example:

document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    // Page became hidden
    console.log('User left the page (tab switch or minimize)');
    // Pause video, animation, etc.
    pauseMyVideo();
  } else {
    // Page became visible again
    console.log('User returned to the page');
    // Resume playback
    playMyVideo();
  }
});

Advantages:

Standard, reliable : W3C standard supported by all modern browsers.

Performance‑friendly : Designed to save CPU and battery resources.

Clear logic : Directly reflects the page’s “visible” state.

Disadvantages:

It cannot distinguish a closed tab from a hidden one; when the user closes the tab, the visibilitychange event may fire (becoming hidden) but we cannot tell whether the tab was closed or merely hidden.

Method 2: beforeunload and unload events – Traditional farewell

These two events fire when the user is actually about to leave the page (close, refresh, or navigate away).

1. beforeunload event

This event triggers when the window, document, and its resources are about to be unloaded. It can be used to ask the user for confirmation before leaving.

Core usage:

Prevent accidental loss of unsaved data; browsers typically show a confirmation dialog.

Code example:

window.addEventListener('beforeunload', () => {
  console.log('User is about to leave or refresh the page');
  // Note: Modern browsers do not allow custom text in the dialog.
});

Note: For security reasons, modern browsers do not allow developers to customize the text shown in the confirmation dialog; only a standard built‑in message is displayed.

2. unload event

This event fires after the page has started unloading. It is the traditional place to perform final cleanup when the user leaves.

Code example:

window.addEventListener('unload', () => {
  console.log('User is closing or leaving the page');
  // Warning: Operations here may not complete!
  // sendAnalyticsData();
});

Major flaw: The unload event is unreliable . Browsers do not wait for asynchronous operations (such as fetch or XMLHttpRequest) inside the handler to finish, so a request to send analytics data is likely to be terminated before completion.

Method 3: navigator.sendBeacon() – Reliable data reporting tool

To solve the unreliability of asynchronous requests in the unload event, the W3C introduced the navigator.sendBeacon() API.

Core concept: sendBeacon() can asynchronously send a small amount of data to the server, and the browser guarantees that it will be queued and sent without blocking or delaying the page unload. Even after the page is closed, the data continues to be sent in the background.

Applicable scenarios:

Reliably send logs, analytics, or statistics when the user leaves the page.

How to use (usually together with unload or pagehide ):

window.addEventListener('pagehide', (event) => {
  // This is a good time to send a beacon regardless of the situation
  navigator.sendBeacon('/log', getAnalyticsData());
});

Using pagehide is more reliable than unload, especially on mobile devices. Therefore, it is recommended to replace unload with pagehide for best compatibility and reliability.

Method 4: pagehide and pageshow events – Handling back‑forward cache (bfcache)

Modern browsers (especially on mobile) introduce a “back‑forward cache” (bfcache). When a user navigates away and later clicks the back button, the browser may restore the previous page from this cache instead of reloading it. In such cases, the unload event may never fire, while the pagehide event can handle the situation.

Core concepts:

pagehide event : Fires when the user navigates away, regardless of whether the page is stored in bfcache.

event.persisted : A property of the pagehide event object; it is true if the page is stored in bfcache, otherwise false.

Code example:

window.addEventListener('pagehide', (event) => {
  if (event.persisted) {
    console.log('Page is entering bfcache');
  } else {
    console.log('Page is being unloaded normally');
  }
  // Send beacon here
  navigator.sendBeacon('/log', getAnalyticsData());
});

Compared to unload, pagehide is more reliable, especially on mobile devices.

Final Recommendations

For visibility detection : Prefer the Page Visibility API .

For reporting data on exit : Use navigator.sendBeacon() inside a pagehide listener for best compatibility and reliability.

To prevent data loss : Use beforeunload only when necessary, as it interrupts user interaction.

Avoid unload unless you only need to run very simple synchronous code; asynchronous network requests are unreliable there.

By combining these modern APIs, we can accurately determine user behavior while maintaining performance and reliability, creating smarter and more user‑friendly experiences.

frontendweb performancepage-visibilitysendBeaconunload events
JavaScript
Written by

JavaScript

Provides JavaScript enthusiasts with tutorials and experience sharing on web front‑end technologies, including JavaScript, Node.js, Deno, Vue.js, React, Angular, HTML5, CSS3, and more.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.