# PWAs on desktop

> How Chrome, Edge, and Safari install a PWA on a desktop OS, what the manifest display fallback chain gives you, and why each browser keeps its own copy.

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'.

## How it works

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.

### Install support in each browser

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](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Installing),
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](https://support.google.com/chrome/answer/9658361)).
- 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](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps/ux),
  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 window you get

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](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/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](https://web.dev/articles/window-controls-overlay),
web.dev).

```json
{
  "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" }]
}
```

### One copy per browser

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).

### Manifest updates on desktop

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](https://web.dev/articles/manifest-updates),
web.dev). `chrome://web-app-internals`, available since Chrome 85, lists the installed
apps with their manifest state and last update check.

## Examples

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

### 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`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/display-mode),
MDN):

```js
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.

### Styling the standalone window from CSS

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

```css
@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`.

## Observed behaviour

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.

:::observed
In Chrome on Windows, macOS, and Linux (English UI), **More** > **Uninstall [app name]**
inside the app window opens a dialog whose confirm button is **Remove** and whose optional
checkbox reads "Also delete data from Chrome." (Chrome Help, read 2026-10). Leaving the
box unticked uninstalls the window but keeps cookies, IndexedDB, and the service worker
registration, so the site is still "installed" from the service worker's point of view on
the next visit in a tab.
:::

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.

## See also

- [Installing and uninstalling web apps](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Installing) (developer.mozilla.org)
- [Use Progressive Web Apps in Microsoft Edge](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps/ux) (learn.microsoft.com)
- [How Chrome handles updates to the web app manifest](https://web.dev/articles/manifest-updates) (web.dev)
- [Desktop PWA installation: Chrome, Edge, and beyond](/reference/installation/desktop-install/)
- [Manifest display modes: standalone vs fullscreen](/reference/manifest/display/)
- [display_override: opting into window-controls-overlay and tabbed modes](/reference/manifest/display-override/)
- [PWAs on Windows](/reference/platforms/windows/)
- [PWAs on Safari for macOS (Dock apps)](/reference/platforms/macos-safari/)