5.1 Page lifecycle: the hidden transition is the contract
The spec's MUST-flush-on-hidden guarantee parsed clause by clause, state-versus-event reasoning, ordering, and mobile reality.
Intermediate
Everything in this series so far has been about getting data accepted. This page is about when the browser promises to actually move it. The answer is a single normative sentence, and it keys on a
state — the document's
visibilityState becoming
"hidden" — not on any event name you might be used to wiring.
The guarantee, verbatim
"The user agent MUST schedule immediate transmission of all beacon requests when the document visibilityState transitions to hidden, and must allow all such requests to run to completion without blocking other time-critical and high-priority work."— Beacon, sendBeacon method
Parse it as three commitments. Immediate transmission: hidden is not a hint, it is a deadline — the browser flushes queued beacons right then, not at some idle moment. Run to completion: the requests are allowed to finish their network work even as the page is being frozen or discarded. And Without blocking: the flush must not starve time-critical work — which is why the same spec text that demands immediacy also allows the user agent to schedule transmission to minimize contention. The guarantee is about ordering against page death, not about microsecond latency.
State transitions, not event names
The spec deliberately names the state, not the DOM event that reports it. In JavaScript you observe the transition with
visibilitychange — and inside the handler you must
re-check the state (
document.visibilityState === 'hidden'), because the same event fires for the visible direction, and handlers that branch on the event name alone double-fire or fire backwards. The runnable example drives the real document of its sandbox frame through the interesting sequences:
Output
Ready to run.
Ordering, mobile reality, and what is guaranteed
The order in which the signals arrive is browser behavior, consistent across modern engines but not spec text, and worth knowing exactly: on desktop navigation the visibility transition fires
before
pagehide, so a flush keyed on hidden is also early enough for any pagehide-dependent bookkeeping. On mobile, app switches and screen locks fire the visibility transition where desktop fires navigation events; a hard OS kill or crash fires
nothing at all — which is the deepest argument for flushing on every hidden transition rather than hoarding data for one final moment that may never be signaled.
Quick check
Which handler body is spec-faithful for the last-gasp flush?
document.addEventListener('visibilitychange', );
Key takeaways
-
The spec's MUST: immediate transmission of all beacons when
visibilityStatetransitions tohidden. -
Key on the state, not the event: inside
visibilitychange, re-checkdocument.visibilityState. - On desktop navigation, hidden fires before pagehide (browser behavior, consistent across engines).
- On mobile, the hidden transition is the reliable signal; hard kills fire nothing — flush early, flush often.
-
The flush guarantee is
sendBeacon-only;
fetch(keepalive)survives teardown but is not spec-bound to flush on hidden.
Thanks for your feedback.
Mark this learning resource complete to track your learning progress.