Skip to content

Instantly share code, notes, and snippets.

@NekR
Last active August 29, 2021 07:10
Show Gist options
  • Save NekR/25420489ba5eea0364d0 to your computer and use it in GitHub Desktop.
Save NekR/25420489ba5eea0364d0 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;
}
@NekR
Copy link
Author

NekR commented Oct 2, 2015

Update 3:
Update info about display-mode media query.

Update 2:
Make history.replaceState(...) even if display-mode exists.

Update 1:
Added history.replaceState(history.state, '', '/'); for app with hash (#) routing.

@Maxou44
Copy link

Maxou44 commented May 13, 2021

This code don't work on Chrome PWA in 2021

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