pwas: detecting mode and installation status



~4 min read


766 words

Things you may want to know about a PWA

PWAs are pretty awesome. This site is one actually (thanks primarily to the hard work of the folks at Gatsby).

As a developer of a PWA there are a number of problems where it’d be useful to have a bit more context on how visitors are actually accessing the site. Are they using it as a site or an app? Where did they launch it from? Did it change?

Said another way, we might want to know:

  1. Is the PWA installed?
  2. How was it launched?
  3. Did the mode change?

Why would you want to know this?

There are a number of different use cases for knowing the mode, some basic ones include:

  1. Should I prompt the user to install the app?
  2. Do users behave differently if they interact with the PWA vs the site?

Detecting if the app is installed

So, how do we tell if the PWA is installed? Wouldn’t it be nice if the browser would just tell you?Well,, one day soon, it just might using the getInstalledRelatedApps API on the Navigator API.

Unfortunately, for now, the browser support is pretty minimal with Chrome for Android and Samsung Internet being the only well supported browsers. Hopefully we’ll settle on a standard soon enough!

The good news is that Mobile Safari has it’s own approach: window.navigator.standalone. This API returns a boolean based on the CSS display mode - but this actually only tells you if the agent is running the app, not whether or not it’s installed (though most of the time they should be the same).

Detecting App Mode

In detecting if the app is installed, I mentioned the standalone on the Navigator API for mobile Safari. This is a non-standard API and seems to only be supported on iOS. So, if that’s not your use case, what are you to do? How can you detect if a visitor is using an installed version of your PWA?

Use display-mode, a CSS media feature. At one point, this wasn’t well supported (and Web.Dev still suggests it’s not supported on Safari), but CanIUse suggests otherwise.

There are four standard display modes. For our purposes, we are most interested in standalone which well describes the PWA use case:

Display mode Description Fallback display mode
fullscreen All of the available display area is used and no user agent chrome is shown. standalone
standalone The application will look and feel like a standalone application. This can include the application having a different window, its own icon in the application launcher, etc. In this mode, the user agent will exclude UI elements for controlling navigation, but can include other UI elements such as a status bar. minimal-ui
minimal-ui The application will look and feel like a standalone application, but will have a minimal set of UI elements for controlling navigation. The elements will vary by browser. browser
browser The application opens in a conventional browser tab or new window, depending on the browser and platform. (none)

There are two situations we’d be interested in understanding which mode the user is in:

  1. On load
  2. On change

Google’s has a useful guide here

Detecting On Load

To detect which mode we’re in on load, we’ll attach a listener to the DOMContentLoaded event:

window.addEventListener("DOMContentLoaded", () => {
  let displayMode = "browser tab"
  if (navigator.standalone) {
    displayMode = "standalone-ios"
  if (window.matchMedia("(display-mode: standalone)").matches) {
    displayMode = "standalone"
  // Log launch display mode to analytics
  console.log("DISPLAY_MODE_LAUNCH:", displayMode)

This code will appropriately track whether the app is running in PWA and specify whether that’s iOS or not.

Detecting Changes

But what if the mode changes? For example, if the user starts in the browser, then installs the app?

window.addEventListener("DOMContentLoaded", () => {
  window.matchMedia("(display-mode: standalone)").addListener((evt) => {
    let displayMode = "browser tab"
    if (evt.matches) {
      displayMode = "standalone"
    // Log display mode change to analytics
    console.log("DISPLAY_MODE_CHANGED", displayMode)

We’re once again adding the listener during the DOMContentLoaded event, but now we’re attaching it to the window matching the display-mode to standalone. If that happens, we can say that the browser changed because it begins in the state of browser.


Related Posts
  • 2021 Daily Journal

  • Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!