Skip to content

Page lifecycle

React to browser-initiated page suspend/resume — pagehide/pageshow and (where supported) freeze/resume — distinct from the Session Core's created/running/stopped/disposed phases.

Previous: Cross-tab · Next: Events

Playground

Open Lifecycle playground → — navigate away and back to observe page:suspend / page:resume / session:restored.

Not the same as Session Core phases

This page covers the Page Lifecycle Module (snapshot.lifecycle, browser bfcache-style suspend/resume). For the session's own createdrunningstoppeddisposed phases, see Session core.

Import path

ts
import { createBrowserLifecycle } from "@jayoncode/browser-lifecycle";

Problem → approach

Without page lifecycle moduleWith page lifecycle events
Raw window.addEventListener("pagehide"/"pageshow", …) per featurepage:suspend / page:resume / session:restored on the lifecycle bus
Back-forward cache (bfcache) restores silently reuse stale statesession:restored signals a pageshow restore explicitly
No normalized "is this page about to be discarded" signalsnapshot.lifecycle — normalized page state
ts
lifecycle.on("page:suspend", () => flushPendingWrites());
lifecycle.on("page:resume", () => refreshStaleData());
lifecycle.on("session:restored", () => rehydrateUi());

Pitfalls

  • freeze / resume document events are non-standard and only fire in some Chromium builds — treat "frozen" as best-effort, not guaranteed.
  • session:restored and page:resume can both fire for the same pageshow event — see below.
  • Always dispose() the session (or unsubscribe) on unmount.

Technical reference

Overview

The Page Lifecycle Module observes window pagehide/pageshow and, when present, document freeze/resume/visibilitychange to report normalized page suspend/resume transitions.

It is responsible only for:

  • reading initial page state
  • listening for the above browser signals
  • reporting normalized lifecycle transitions to Session Core
  • cleaning up browser listeners

Architecture

text
pagehide / pageshow / freeze / resume
  -> lifecycle adapter
  -> Page Lifecycle Module
  -> Session Core internal event
  -> public BrowserLifecycle events

Implementation is split into:

  • src/modules/lifecycle/lifecycle-adapter.ts
  • src/modules/lifecycle/lifecycle-module.ts
  • src/modules/lifecycle/types.ts
  • src/modules/lifecycle/index.ts

Session Integration

The Page Lifecycle Module reports changes to Session Core through the internal internal:lifecycle-changed event. Session Core then:

  • updates snapshot.lifecycle
  • records event timestamps
  • emits page:suspend, page:resume, and/or session:restored
  • preserves lifecycle-first ordering by flushing startup lifecycle events after session:started

Public Events

EventFires whenMetadata
page:suspendpagehide (→ "hidden") or freeze (→ "frozen"){ lifecycleSignal, reason }
page:resumepageshow, resume, or a visibilitychange that reports "active"{ reason, resumeSource }
session:restoredSpecifically a pageshow — fires immediately before page:resume{ persisted: false, restoreSource: "pageshow" }
ts
lifecycle.on("session:restored", () => {
  // Runs first for a pageshow (e.g. back/forward navigation, bfcache restore)
});
lifecycle.on("page:resume", () => {
  // Runs for every resume, including the same pageshow
});

Snapshot field

ts
const { lifecycle: pageState } = lifecycle.getSnapshot();
// "active" | "discarded" | "frozen" | "hidden" | "passive" | "terminated" | "unknown"

The current adapter only ever produces "active", "frozen", "hidden", or "unknown""discarded", "passive", and "terminated" are reserved type members for future adapter coverage and are not emitted by this module today.

Initial State and Duplicates

Initial page state is detected during module initialization and stored in the snapshot.

When emitInitialState is true, the module replays that startup state as a public event after session:started (with reason: "initial").

Duplicate browser callbacks that do not change normalized state are suppressed.

SSR Safety

Construction remains SSR-safe:

  • no window / document access during Session Core construction
  • browser APIs are touched only through the adapter during module initialization and start

If page lifecycle observation is unavailable, the module disables itself without throwing.

Browser Compatibility

This module depends on the pageLifecycle capability — supportsPageLifecycle(), which requires window to expose onpagehide and onpageshow.

When that capability is unavailable:

  • snapshot.lifecycle remains "unknown"
  • no lifecycle listeners are attached
  • no page:suspend / page:resume / session:restored events are emitted

Events →

An ecosystem of independent, headless TypeScript libraries engineered for modern web development. Every package includes interactive playgrounds and documentation that evolves alongside the code.