# Desktop installation

> How Chrome, Edge, Safari, and Firefox install a web app on Windows, macOS, Linux, and ChromeOS, where it lives on each OS, and how a page detects it.

Desktop installation turns a site into an OS-level app with its own window, launcher entry, and taskbar or Dock presence. Chrome 73 and Edge 79 install from the address bar, the browser menu, or `beforeinstallprompt` on Windows, macOS, Linux, and ChromeOS; Safari 17 on macOS 14 installs through File › "Add to Dock"; Firefox 143 on Windows pins a site as a taskbar web app (Firefox 143 release notes); earlier desktop Firefox and Safari have no install path at all, and no browser other than Chromium fires `beforeinstallprompt` (BCD `api.BeforeInstallPromptEvent`).

## How it works

A desktop install has three parts: a trigger, an OS registration, and a window mode. The trigger is either the browser's own surface (Chrome and Edge show an install icon at the right end of the address bar once the installability criteria are met, plus an install entry in the three-dot menu; Safari uses File › "Add to Dock"; Firefox 143 uses an address-bar icon) or, on Chromium only, the page's own button backed by a stored `BeforeInstallPromptEvent`. There is no mini-infobar on desktop; without the event, a page has no ambient banner to suppress.

On accept, the browser writes an OS launcher entry so the app can start without the browser being open first:

| OS | What the browser creates | Management surface |
|---|---|---|
| Windows (Chrome, Edge) | Start-menu shortcut; the app appears in the taskbar and Alt+Tab. Edge also registers it under Settings › Apps › "Installed apps". | `chrome://apps`, `edge://apps` |
| macOS (Chrome, Edge) | An app bundle in `~/Applications/Chrome Apps.localized/` or `~/Applications/Edge Apps.localized/`, visible in Spotlight and the Dock. | `chrome://apps`, `edge://apps` |
| macOS (Safari 17+) | An app in `~/Applications/` opened by a Safari-based window without tabs or an address bar; cookies and storage are separate from Safari's. | Delete the app from Applications |
| Linux (Chrome, Edge) | A `.desktop` entry in `~/.local/share/applications/`, picked up by GNOME and KDE launchers. | `chrome://apps`, `edge://apps` |
| ChromeOS | A launcher entry that can be pinned to the shelf; Play Store listings via TWA are separate. | Launcher › right-click › Uninstall |

The window mode comes from the manifest. `display: standalone` yields a window with the OS title bar and no browser chrome; `minimal-ui` adds back, forward, and reload; `display_override: ["window-controls-overlay"]` lets the page draw into the title-bar area while the OS keeps the close, minimise, and maximise buttons, exposing the remaining area through `env(titlebar-area-x)`, `env(titlebar-area-y)`, `env(titlebar-area-width)`, and `env(titlebar-area-height)` (Chrome 105, Edge 105, desktop only). Window title and icon come from `name` and `icons`; a new window per launch versus focusing the existing one is decided by `launch_handler.client_mode`.

The trade-off against a browser tab is concrete: the app loses the address bar, tab strip, and extensions (Chrome runs installed apps without extension toolbars), and gains a separate window in Alt+Tab or Cmd+Tab, OS notifications attributed to the app's name and icon, badging, file and protocol handling, and launch from the OS. Cookies and storage are shared with the installing browser profile on Chrome and Edge, so sign-in carries over; on Safari 17 they are not, so the user signs in again inside the app.

## Observed behaviour

The installed artefact is visible on disk, which is the quickest way to confirm what a given browser did.

:::observed
After installing a site from Chrome on macOS (English UI), Finder shows `~/Applications/Chrome Apps.localized/<App name>.app`, and `chrome://apps` lists the app with an "Uninstall `<App name>`…" item in its context menu; on Linux the same install writes `~/.local/share/applications/chrome-<app id>-Default.desktop`, where `<app id>` is the 32-letter identifier `chrome://apps` uses in its links. Launching the app opens a window whose title bar shows the manifest `name` and no address bar, and `matchMedia('(display-mode: standalone)').matches` evaluates to `true` in its DevTools console.
:::

A page can tell that it is running installed, and which window mode it got, without any browser-specific API. The media query reports the mode the browser applied, which may differ from the one requested when `display_override` lists several.

```js
const mode = ['window-controls-overlay', 'standalone', 'minimal-ui', 'fullscreen']
  .find((m) => matchMedia(`(display-mode: ${m})`).matches) ?? 'browser';

if (mode !== 'browser') {
  installButton.hidden = true;              // already installed and launched as an app
}
if (mode === 'window-controls-overlay') {
  document.documentElement.classList.add('wco');   // draw into the title-bar area
}
```

Safari 17 and Firefox 143 apps also report `standalone` here, so the check needs no user-agent branch; what differs per browser is only how the user got there.

## See also

- [Installing and uninstalling web apps](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Installing) (developer.mozilla.org)
- [Progressive Web Apps on Windows: the user experience](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/ux) (learn.microsoft.com)
- [Firefox 143.0 release notes](https://www.mozilla.org/en-US/firefox/143.0/releasenotes/) (mozilla.org)
- [beforeinstallprompt event](/reference/installation/install-prompt/)
- [Installability criteria](/reference/installation/installability-criteria/)
- [Uninstalling an installed PWA](/reference/installation/uninstall-retention/)
- [`display_override` manifest member](/reference/manifest/display-override/)
- [PWAs on macOS Safari](/reference/platforms/macos-safari/)