How to Log Out on Tab Close but Not on Refresh Using the pageswap Event
Logging out on tab close while preserving the session on refresh is a common requirement in enterprise web apps, and most existing solutions are unreliable guesses. The `pageswap` event provides the first browser-native signal to make this distinction correctly, but the event order is unintuitive and easy to get wrong.
For years, frontend developers have tried to tell whether a user is closing a tab or refreshing the page, mostly through fragile heuristics like mouse coordinates or timing gaps. Those guesses fail because the browser withholds the user's intent from the page. The `pageswap` event changes that: its `activation.navigationType` returns `"reload"` for refreshes, providing a direct signal. The catch is event ordering. `beforeunload` fires first to show the native confirmation dialog, but `pageswap` only fires after the user confirms. Any decision to log out must wait for `pagehide`, where the reload flag is finally available. A working implementation uses three listeners: `beforeunload` to trigger the dialog, `pageswap` to capture the navigation type, and `pagehide` to conditionally send a `keepalive` logout request only when the tab is actually closing.
The core difficulty is not a missing API but a timing problem: `beforeunload` asks the question before the browser supplies the answer.
The fact that Chrome's native dialog text differs between refresh and close was the clue that the browser internally knows the operation type, even though older APIs never exposed it.
Many widely circulated solutions — time deltas, mouse position, PerformanceNavigationTiming — all read past state rather than current intent, making them fundamentally unreliable for this use case.
The `pageswap` event is part of the newer View Transitions API surface and is not yet universally supported, so a compatibility check is mandatory.