Measure PWA display, install, and offline events
Published
PWA analytics can record the active display mode and a successful appinstalled event
received by the current page. It can also record browser-reported online or offline transitions.
Keep these signals separate; none reliably proves installation across browsers.
Browser and platform support
Section titled “Browser and platform support”The CSS display-mode media feature can distinguish modes such as browser and standalone
as well as minimal-ui and fullscreen. The active mode is not the same as installation status: MDN
notes that an installed PWA can open in a browser tab and that an ordinary page can enter
fullscreen.
MDN marks appinstalled as limited availability. It fires when a browser successfully
installs a page as an application, but code that never receives it cannot conclude that the
app is not installed. The online and offline events follow changes to navigator.onLine;
that value is a network hint, not a reachability check for the analytics endpoint.
Choose the event shape
Section titled “Choose the event shape”Keep the collected fields tied to the documented signal. The example sends an event name with an ISO timestamp and a small detail object. It does not attach an account identifier or URL query. Form values are also excluded. Add collection and retention rules that match your product’s privacy policy before deploying an analytics endpoint.
Examples
Section titled “Examples”Records stay in a local backlog until the application submits them.
Record display mode at page load
Section titled “Record display mode at page load”Test the documented modes in order and record browser as the fallback. matchMedia is also
used to listen for a later mode change.
const displayQueries = [ ['window-controls-overlay', '(display-mode: window-controls-overlay)'], ['fullscreen', '(display-mode: fullscreen)'], ['standalone', '(display-mode: standalone)'], ['minimal-ui', '(display-mode: minimal-ui)'],];
function activeDisplayMode() { if (!('matchMedia' in window)) return 'browser'; return displayQueries.find(([, query]) => matchMedia(query).matches)?.[0] ?? 'browser';}
function record(name, detail = {}) { const event = { name, detail, occurredAt: new Date().toISOString() }; const backlog = JSON.parse(localStorage.getItem('pwa-analytics') ?? '[]'); backlog.push(event); localStorage.setItem('pwa-analytics', JSON.stringify(backlog));}
record('pwa_display_mode', { mode: activeDisplayMode() });
if ('matchMedia' in window) { for (const [, query] of displayQueries) { matchMedia(query).addEventListener('change', () => { record('pwa_display_mode', { mode: activeDisplayMode() }); }); }}The storage in this example is a local backlog. Replace record() with the ingestion and
retry policy used by your application.
Record installation and connectivity events
Section titled “Record installation and connectivity events”Feature-detect appinstalled before adding its listener. The fallback records that this page
cannot observe the event; it does not record an installation failure. The online state is
recorded as the browser reports it.
if ('onappinstalled' in window) { window.addEventListener('appinstalled', () => { record('pwa_appinstalled'); });} else { record('pwa_appinstalled_unavailable');}
if ('ononline' in window && 'onoffline' in window) { window.addEventListener('online', () => { record('pwa_connectivity', { state: 'online' }); }); window.addEventListener('offline', () => { record('pwa_connectivity', { state: 'offline' }); }); record('pwa_connectivity', { state: navigator.onLine ? 'online' : 'offline', });} else { record('pwa_connectivity_unavailable');}An offline record should be queued locally because the analytics server may not be
reachable. A later application request can submit the backlog and remove only the records the
server accepts.
Interpret the signals
Section titled “Interpret the signals”pwa_display_modedescribes the top-level browsing context at the time it was sampled.pwa_appinstalledmeans this page received the documented successful-install event.pwa_appinstalled_unavailablemeans this page could not attach that event listener.pwa_connectivityreflectsnavigator.onLineand its events, not a successful request to the application or analytics server.
Keep those meanings separate in reports. For example, do not rename a standalone sample to
“installed user”, and do not treat absence of pwa_appinstalled as an uninstall.