Web App Manifest and JavaScript PWA Install State

This article provides an overview of the Web App Manifest and explains how JavaScript interacts with Progressive Web App (PWA) installation states. You will learn the purpose of the manifest file, how to detect if a PWA is currently running as an installed application, and how to programmatically control and listen to the installation lifecycle using modern browser APIs.

What is the Web App Manifest?

The Web App Manifest is a JSON-based configuration file that informs the browser about how your Progressive Web App should behave when installed on a user’s mobile or desktop device. It provides essential metadata that gives a web application the look and feel of a native application.

To link a manifest to your HTML document, add a <link> tag within the <head> section:

<link rel="manifest" href="/manifest.json">

Key Properties in the Manifest

How JavaScript Interacts with Installed PWA State

JavaScript provides several APIs and event listeners to detect installation status, handle installation prompts, and track changes to the app’s display state.

1. Detecting if the PWA is Running in Installed Mode

You can determine if a user is currently accessing the application inside an installed window versus a standard browser tab using the matchMedia API to query the display-mode media feature.

function isRunningStandalone() {
  const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
  const isIOSStandalone = window.navigator.standalone === true; // Fallback for iOS Safari
  return isStandalone || isIOSStandalone;
}

if (isRunningStandalone()) {
  console.log('App is running in standalone (installed) mode.');
} else {
  console.log('App is running in a browser tab.');
}

You can also listen for dynamic changes to the display mode:

window.matchMedia('(display-mode: standalone)').addEventListener('change', (evt) => {
  if (evt.matches) {
    console.log('App entered standalone mode');
  }
});

2. Handling the Custom Install Prompt

Supported Chromium-based browsers fire the beforeinstallprompt event before showing the default installation prompt. JavaScript can intercept this event to provide a custom in-app install button.

let deferredPrompt;
const installButton = document.getElementById('install-btn');

window.addEventListener('beforeinstallprompt', (e) => {
  // Prevent the default mini-infobar from appearing
  e.preventDefault();
  // Stash the event so it can be triggered later
  deferredPrompt = e;
  // Update UI to notify the user they can install the PWA
  installButton.style.display = 'block';
});

installButton.addEventListener('click', async () => {
  if (!deferredPrompt) return;

  // Show the native install prompt
  deferredPrompt.prompt();

  // Wait for the user to respond to the prompt
  const { outcome } = await deferredPrompt.userChoice;
  console.log(`User response to the install prompt: ${outcome}`);

  // Clear the saved prompt reference
  deferredPrompt = null;
  installButton.style.display = 'none';
});

3. Detecting a Completed Installation

The appinstalled event fires immediately after the user has installed the application, whether through the browser interface or via a custom installation button.

window.addEventListener('appinstalled', () => {
  console.log('PWA was successfully installed.');
  // Log the event to analytics or hide installation-related UI elements
});

The navigator.getInstalledRelatedApps() API allows a web app to check if its corresponding native app or PWA is already installed on the client machine. This requires corresponding related_applications entries in the manifest.

if ('getInstalledRelatedApps' in navigator) {
  navigator.getInstalledRelatedApps().then((relatedApps) => {
    if (relatedApps.length > 0) {
      console.log('App is already installed on the device:', relatedApps);
    }
  });
}