Skip to content

Instantly share code, notes, and snippets.

@resultakak
Forked from NekR/standalone.md
Created February 17, 2021 22:05
Show Gist options
  • Save resultakak/bf4cff1494488cdc2047c4a8fb1c852f to your computer and use it in GitHub Desktop.
Save resultakak/bf4cff1494488cdc2047c4a8fb1c852f to your computer and use it in GitHub Desktop.
How to detect if app was run in standalone mode in Chromium (with manifest.json)

Hacky way to detect if app was launched in standalone mode in Chromium (with manifest.json) without problems with AppCache (and possibly ServiceWorker).

As suggested on one of the Google Developers sites, one may use search params in start_url of the manifest.json to notify page about that it was launched in standalone mode: start_url: '/?standalone'.

This works well unless your page uses AppCache (or ServiceWorker). In case of AppCache, every url with different search params is treated as separate entry in cache. So if you have listed / path in AppCache and naively used start_url: '/?standalone' in your manifest.json, then your Web App won't work offline from the Home Screen.
With ServiceWorker, however, there is few ways to fix this directly in its code, like ignoreSearch option for match() method (currently not supported in Chromium) or just traversing cache entries manually and comparing new URL(event.request.url).pathname with stored request new URL(...).pathname.

The fix

// manifest.json
{
  ...,
  "start_url": "/#:standalone:",
  ...
}
function detectStandalone() {
  const hash = window.location.hash;
  let standalone = false;

  if (hash === '#:standalone:') {
    // first run (open app) in standalone mode
    // cache state in sessionStorage

    standalone = true;
    sessionStorage.setItem(':standalone:', '1');
    // remove hash part from the url before actual app start,
    // in case if your app uses hash (#) routing
    history.replaceState(history.state, '', '/');
  } else if (sessionStorage.getItem(':standalone:')) {
    // second and subsequent runs (reloads)
    // sessionStorage is unique per tab and Home Screen app is just a
    // chrome-less tab. So it's safe to assume
    // that user is still in standalone mode

    standalone = true;
  } else {
    // neither first, nor subsequent standalone runs, normal mode
    // do nothing
  }

  return standalone;
}

display-mode media query

Chromium has display-mode media query in trunk now and it's already available in Chrome Canary.

It could be used like this:

@media (display-mode: standalone) {
  /* do something */
}

So once feature will became stable (matchMedia('(display-mode)').matches does not works correctly yet), you could (and should) use it from JS to detect standalone mode of the app. And it's safe to include code of display-mode check into your code right now.

All together

Here is full code with display-mode media query and previous hacky way:

function isWebAppStandalone() {
  const STANDALONE = ':standalone:';
  const hash = window.location.hash;

  let standalone = false;

  if (hash === '#' + STANDALONE) {
    standalone = true;
    history.replaceState(history.state, '', '/');
  }

  if (matchMedia('(display-mode)').matches) {
    return matchMedia('(display-mode: standalone)').matches;
  }

  if (standalone) {
    sessionStorage.setItem(STANDALONE, '1');
  } else if (sessionStorage.getItem(STANDALONE)) {
    standalone = true;
  }

  return standalone;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment