Time to unload your unload events

If you're still depending on unload events in your code, you need to act NOW. Chrome is starting to kill these handlers this month, potentially breaking your website's analytics and data-saving features. These events were never reliable on mobile, Safari already ignores them, and soon desktop Chrome will too. Here's why moving to better alternatives won't just save your code—it'll actually improve your site's performance.

Time to unload your unload events

As a full-stack developer who's been in the trenches for over two decades (I'm old people!), I've seen browser APIs come and go. Today, I want to talk about an old friend that's on its way out: the unload event. If you've been hanging onto this event in your codebase, it's time we had a chat about why you should say goodbye to the unload event (in case you didn't do so already).

What's happening with the unload event?

Google Chrome is gradually phasing out the unload event starting this month (March 2025). The rollout begins with just google.com, then expands to more top sites, and eventually reaches all websites by April 2026. This isn't a sudden change - Chrome has been telegraphing this move since 2019 when they announced their back/forward cache implementation plans.

Why I won't miss the unload event

Let's be honest - the unload event has been more trouble than it's worth for years now. Here's why I'm happy to see it go:

It was never reliable to begin with

We've all been there. You add an unload handler to save some critical user data, only to discover later that it didn't fire half the time on mobile devices. The reason? Mobile browsers often don't trigger unload when tabs are backgrounded and killed by the OS. What seemed like a safety net was actually full of holes.

It kills performance optimizations

The back/forward cache (bfcache) is one of the best performance improvements browsers have implemented in recent years. It creates an instant back/forward navigation experience by freezing a page in memory. The problem? Pages with unload handlers can't use this cache, forcing users to wait for a full page reload when navigating.

Modern browsing patterns have changed

Think about how you use browsers today compared to 15 years ago. We have dozens of tabs open across multiple windows. We leave tabs dormant for days. We switch devices mid-session. The idea that there's a clean "unload" moment when a user leaves your page is a relic of a simpler web.

The deprecation schedule

Chrome is taking it slow, which gives us time to adapt:

  • March 2025: Just google.com loses unload support (Chrome 135)
  • April-June 2025: Expanding to 50 top websites (Chrome 136-138)
  • August 2025-April 2026: Gradual rollout to all sites (Chrome 140-147)

Better ways to handle page exits

So what should you use instead? Here are three better options I've adopted in my own projects:

The visibilitychange event

The visibilitychange event (part of Page Visibility API) is my go-to replacement. It fires when a user switches tabs or minimizes the browser - often the last reliable chance to save data:

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    // Last chance to save data - the user is navigating away
    saveUserProgress();
    
    // Don't count on getting more execution time after this
  }
});

feature: Page visibility
Baseline Widely available

Page visibility is well established and works across many devices and browser versions.

  • Supported as of Chrome 33, Edge 12, Firefox 18 and Safari 7
  • Resulting in full support since July 29, 2015
  • Continue reading about page visibility

Register to RUMvision to see more resources and learn if your website visitors would already benefit from this feature today.

The pagehide event

Similar to unload but compatible with the back/forward cache, the pagehide event (part of PageTransitionEvent) can be used:

window.addEventListener('pagehide', (event) => {
  // Clean up things that would break if the page gets frozen and restored later
  // event.persisted tells you if the page might be restored from bfcache later
  if (event.persisted) {
    console.log('This page *might* be entering the bfcache.');
  } else {
    console.log('This page will unload normally and be discarded.');
  }
});

This code can be found in the bfcache article on web.dev.

feature: Page transition events
Baseline Widely available

Page transition events is well established and works across many devices and browser versions.

Register to RUMvision to see more resources and learn if your website visitors would already benefit from this feature today.

Selective use of beforeunload

The beforeunload event (which shows a browser native "Leave site?" dialog) isn't being deprecated, but you should still use it sparingly as beforeunload is still unreliable. That's because the beforeunload event might never be fired in some scenarios. and suggests to avoid using it unless absolutely necessary.

function updateLeaveWarning(hasUnsavedChanges) {
  // Only add the warning when there are unsaved changes
  if (hasUnsavedChanges) {
    window.addEventListener('beforeunload', showWarning);
  } else {
    window.removeEventListener('beforeunload', showWarning);
  }
}

function showWarning(e) {
  // Browsers standardize the dialog message regardless of what text we provide
  e.preventDefault();
  return e.returnValue = 'Are you sure you want to exit?';
}

// Add listeners to track changes
formElement.addEventListener('change', () => {
  updateLeaveWarning(true);
});

saveButton.addEventListener('click', () => {
  updateLeaveWarning(false);
});

feature: beforeunload
Limited availability

beforeunload is not Baseline because it does not work in some of the most widely-used browsers.

Register to RUMvision to see more resources and learn if your website visitors would already benefit from this feature today.

fetchLater API

Another promising alternative, particularly for analytics and monitoring use cases, is the fetchLater API. Coming out of origin trial phase, this (Chromium >= 135) API directly addresses one of the most common unload event use cases.

Back in the days, performance monitoring solutions like New Relic and Dynatrace ended up using the unload event as a means to dispatch analytics data by sending a beacon when unload was fired. This approach created a difficult trade-off: despite being performance monitoring solutions, they were either actively impacting site performance (by blocking the back/forward cache) or having their data collection silently fail when unload events didn't fire.

The fetchLater API solves this by allowing developers to queue network requests that will be sent even after a page is closed:

// Queue an analytics request that will be sent even if the page is closed
navigator.fetchLater.fetch('https://analytics.example.com/collect', {
  body: JSON.stringify({event: 'page_exit', duration: pageSessionTime}),
  method: 'POST',
  keepalive: true
});

Unlike the unload event, the fetchLater API approach allows the browser to optimize page transitions while still ensuring your analytics data gets sent reliably.

feature: fetchLater
Limited availability

fetchLater is not Baseline because it does not work in some of the most widely-used browsers.

  • Only supported in Chrome 135 and Edge 135
  • Continue reading about 1647

Register to RUMvision to see more resources and learn if your website visitors would already benefit from this feature today.

Finding unload usage in your codebase

Before you can fix the problem, you need to find it. Here's how I hunt down unload usage:

Look for direct event handlers

Search your codebase for:

  • addEventListener('unload'
  • onunload
  • .unload( (for jQuery)

Check for back/forward cache eligibility

Open Chrome DevTools, navigate to Application > Background services > Back/forward cache, and click "Test back/forward cache". If your page fails the test due to an unload handler, Chrome will tell you.

Disabling and reporting

As a site owner, you can choose to disable the use of the unload event altogether. This will keep your site eligible for the back/forward cache, even if existing or newly added (third-party) scripts start to use the unload handler again.

Alternatively, you can already start detecting which scripts are still actively using the unload handler. This can be achieved by using the Reporting API and sending data to an endpoint of your choice.

This GitHub explainer document covers both scenarios.

Options while you migrate

If you need more time, Chrome offers several ways to postpone the inevitable:

For developers testing locally

Enable the Chrome flag at chrome://flags/#deprecate-unload to simulate the future behavior now and test your fixes.

For website owners

Use the Permissions Policy header to explicitly opt-in to keep using unload during the transition:

Permissions-Policy: unload=self

For enterprise IT

If you manage a corporate environment with legacy applications, look into the ForcePermissionPolicyUnloadDefaultEnabled enterprise policy to delay the deprecation.

Let's face reality

The deprecation of unload is actually a helpful reminder that we never had as much control over the web page lifecycle as we thought. Users have always been able to kill our pages without warning - by force-quitting browsers, running out of battery, or losing network connections.

Good web development means designing for resilience. Save data early and often. Use client-side storage wisely. Sync to servers when connections are available. These practices have always been better than relying on an unload event that might never come.

So while it might be annoying to update your code now, it's pushing us toward more reliable patterns that better match how people actually use the web today. That's something we can all get behind, right?

Share blog post