A page can tell if it runs as an installed PWA or in a normal tab: the display-mode media query, iOS navigator.standalone, and what each context reveals.
Open the same web app from a Home Screen icon and from a browser tab, and the page itself often behaves differently: no address bar, its own window, sometimes a different layout. The page didn't guess. It asked the browser one question: am I running as an installed app, or as an ordinary tab? Here is how that check works, where it breaks, and why it also matters for fingerprinting.
Key Takeaways
- The standards-based check is the
display-modemedia feature:window.matchMedia('(display-mode: standalone)').matchesistruewhen the page runs as an installed app window with no browser UI. - The mode comes from the
displaymember of the web app manifest, but what the page sees is the mode it was actually launched in, not what the manifest asked for. - iOS Safari adds a non-standard
navigator.standaloneboolean. It is an Apple WebKit property: on iOS it istruefrom the Home Screen,falsein a tab, andundefinedalmost everywhere else. - Robust code combines both signals, and treats the result as a hint about presentation, never as proof of identity or security.
- An installed context is much rarer than a tab, and a user-agent that claims iOS but lacks
navigator.standaloneis internally inconsistent, so detection tools read these signals too.
The Standard Way: the display-mode Media Query
A manifest declares how an installed app should look through its display member. MDN's display-mode reference lists the values a page can match against; these four are the core ones (newer modes such as window-controls-overlay and picture-in-picture also exist):
| Value | What the user sees |
|---|---|
fullscreen | The whole screen, no system UI |
standalone | Its own app window, no browser address bar or tabs |
minimal-ui | An app-like window with a minimal set of browser controls |
browser | A normal browser tab or window |
The same media feature works in CSS and in JavaScript:
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
// react if the user installs or switches while the page is open
window.matchMedia('(display-mode: standalone)')
.addEventListener('change', (e) => console.log('standalone:', e.matches));
@media (display-mode: standalone) {
.install-banner { display: none; }
}
Two details trip people up. First, the query reports the current mode: an app whose manifest says standalone but that is opened in a tab by a link still reports browser. Second, browsers fall back through the chain the spec defines, so a fullscreen request on a platform that doesn't allow it can end up as standalone or minimal-ui. Test for the mode you care about; don't assume the manifest value.
The iOS Extra: navigator.standalone
Safari on iOS predates the media feature and ships its own flag:
// true: launched from the Home Screen; false: in a Safari tab;
// undefined: not iOS WebKit
const iosStandalone = navigator.standalone === true;
The distinction between false and undefined is the useful part. A browser that implements the property but isn't launched from the Home Screen returns false, while Chrome on Windows or Android returns undefined. Because every iOS browser is built on WebKit, the property comes from the engine, not from the app's brand name.
A practical detector combines both:
const installed =
window.matchMedia('(display-mode: standalone)').matches ||
navigator.standalone === true;
Why Sites Check
- Analytics. Splitting sessions by installed vs. tab shows whether the install is actually used and whether installed users behave differently.
- Install prompts. There is no point showing "Add to Home Screen" to someone who already did.
- UI differences. With no back button or address bar, a standalone app needs its own navigation, and the page may need extra safe-area padding.
- Debugging. "Works in the tab, breaks as an app" reports are far easier to triage when the page records which context it ran in.
Standalone vs. WebView vs. Tab
Readers often mix up three contexts that all lack the usual browser chrome:
| Context | display-mode: standalone | Typical tell |
|---|---|---|
| Ordinary tab | No (browser) | Full browser UI |
| Installed PWA | Yes | Own window, launched from an icon |
| In-app browser (WebView) | Usually no | wv token or app-specific user-agent markers |
An in-app browser is a page rendered inside another app such as a social network, and the host app, not the user, decided to show it there. An installed PWA is something the user explicitly added. A page that only checks "is there an address bar?" would confuse the two, which is why the media query and the user-agent signals are used together.
The Fingerprinting Angle
Is this signal identifying? On its own, barely: it is one bit. But it adds to the picture in two ways. The installed context is rarer than a tab, so it narrows the crowd a little, in the same way other media-feature signals do. And it is a consistency check: a user-agent that says iPhone while navigator.standalone is undefined, or an "iOS" profile on a desktop engine, is the kind of mismatch described in the fingerprint consistency checklist. It is one more small piece of the mobile fingerprint surface.
For background on the app model itself, MDN's Progressive web apps guide covers manifests, installability, and how browsers present installed apps.
See It Yourself
Open BrowserInsight's webview check in a normal tab and note the result. Then add the site to your Home Screen (or install it from the browser menu), open it from the icon, and run it again. The "Standalone (PWA) mode" signal reads exactly the two checks above, so you can watch it flip. Our kernel check also reads navigator.standalone as one of its WebKit signals.
Frequently Asked Questions
Does display-mode work on desktop?
Yes. Desktop Chromium browsers can install a site as an app window, and that window matches (display-mode: standalone). Support and install flows vary by browser, so feature-test rather than assuming.
Can a page tell whether the app could be installed?
Not through these signals. They only describe how the page is running now. Install prompts rely on separate, browser-specific events, and a tab user who never installed looks like any other tab.
Is navigator.standalone still needed?
Modern iOS also supports the media query, so new code can lean on display-mode. Many sites still check both, because it costs one line and covers older behaviour.
Can users fake it?
A user-agent switcher or automation tool can spoof a string, but a consistent fake must also match the engine's real properties, which is what consistency checks look for. Treat the signal as a presentation hint, not a security boundary.
Conclusion
Detecting a PWA in standalone mode takes two lines: the display-mode media query plus, for iOS Home Screen apps, navigator.standalone. What the page learns is how it was launched, which is useful for analytics, install prompts and layout, and slightly informative for fingerprinting, mostly as a consistency check.


