Skip to content

Platforms · Platform

PWAs on desktop

Published

On Windows, macOS, Linux, and ChromeOS a PWA installs from the browser into a window of its own, with an entry wherever the OS lists applications, and the browser that performed the install owns the copy: Chrome, Edge, and Safari each install, update, and uninstall their own instance, and none of them sees the others’.

The desktop install is a browser feature layered on the OS, which is why the entry point, the window chrome, and the uninstall path differ per browser while the manifest that drives them is shared.

In Chrome and Edge the prompt is in the address bar: when a site meets the installability criteria and is not yet installed by that browser, an install icon appears there, and selecting it shows a confirmation (Installing and uninstalling web apps, MDN). The exact labels (English UI):

  • Chrome: an Install button at the right end of the address bar, or More > Cast, save, and share > Install page as app… for any page, manifest or not (Chrome Help).
  • Edge: the App available icon in the address bar, then Install; the post-install dialog offers Pin to taskbar, Pin to Start, and Auto-start on device login (Use Progressive Web Apps in Microsoft Edge, Microsoft Learn).
  • Safari: File > Add to Dock… on macOS 14 (Sonoma) with Safari 17 and later, for any site with or without a manifest; before macOS 14, Safari was the one macOS browser that could not install (MDN).
  • Firefox: no manifest-driven install on desktop; MDN points to a third-party extension.

The manifest display member asks for fullscreen, standalone, minimal-ui, or browser (the default when absent). A browser that does not implement the requested mode walks the fallback chain fullscreen → standalone → minimal-ui → browser, may override the mode for security reasons, and may let the user switch modes (Manifest display, MDN). standalone gives an OS window without the address bar; it is still a browser window, with the installing browser’s engine, profile, and extensions behind it. Chromium 104 added "display_override": ["window-controls-overlay"], which removes the title bar and lets the page draw into it on desktop OSes only (Customize the window controls overlay of your PWA’s title bar, web.dev).

{
"name": "HackerWeb",
"short_name": "HackerWeb",
"start_url": "/index.html",
"display": "standalone",
"background_color": "white",
"icons": [{ "src": "images/icons/homescreen192.png", "sizes": "192x192", "type": "image/png" }]
}

An install belongs to the browser that made it. Install the same site from Edge and then from Chrome and you have two apps, each in that browser’s own folder where the OS keeps applications, with no shared cookies, storage, or service worker between them; Edge will offer to open its copy while Chrome keeps offering to install (MDN). Management pages are per browser too: edge://apps in Edge, chrome://apps in Chrome. Uninstall in Chrome runs from inside the app window: More > Uninstall [app name] > Remove (Chrome Help).

Chrome on desktop re-fetches the manifest when the app launches if it has not been checked since the browser started or in the last 24 hours, and applies a changed name, short_name, display, scope, shortcuts, start_url (only with a manifest id), theme_color, or file_handlers after every app window has closed; icons changes are not applied on desktop, and a user who switched the app to open in a tab keeps that choice when display changes (How Chrome handles updates to the web app manifest, web.dev). chrome://web-app-internals, available since Chrome 85, lists the installed apps with their manifest state and last update check.

Both snippets read the mode the browser applied, which can differ from the manifest’s request.

Branching on the applied display mode from script

Section titled “Branching on the applied display mode from script”

Window.matchMedia() evaluates the display-mode media feature, which reflects the mode in use rather than the one requested (display-mode, MDN):

function isStandaloneDisplayMode() {
if (!('matchMedia' in window)) {
return false; // no media query support: treat the page as an ordinary tab
}
return window.matchMedia('(display-mode: standalone)').matches;
}
if (isStandaloneDisplayMode()) {
document.querySelector('#install-banner').hidden = true;
} else {
document.querySelector('#install-banner').hidden = false;
}

The fallback branch keeps the install banner visible, which is the safe default: a browser without matchMedia is not running the page in an app window.

The same media feature works without script, which is useful for spacing that only makes sense when there is no address bar:

@media (display-mode: standalone) {
body {
padding-top: env(titlebar-area-height, 0px);
}
}

env(titlebar-area-height) is defined only under window controls overlay; the 0px default keeps the rule harmless in plain standalone.

Because installs are per browser, the uninstall dialog is the browser’s, not the OS’s, and it decides what happens to the site’s data.

Edge’s equivalent lives on edge://apps (Details > Uninstall), with a prompt to decide whether app history and data go too (Microsoft Support). A PWA that stores state should treat a reinstall as a possible continuation, not a first run.

Specifications

SpecificationStatus
None.