Concept in Installation
Several PWAs on one origin
Published
A single origin can host distinct installed web applications with separate manifests. Use the manifest
id for application identity and scope for the navigation boundary of each
application context (Web Application Manifest, W3C).
How it works
Section titled “How it works”A processed id is a URL with the same origin as the processed start_url. A user agent should
treat a manifest with an identity that does not match an installed application as a distinct
application. A matching identity signals that the manifest replaces the installed application’s
manifest. If id is missing, empty, invalid, or cross-origin, its processed value remains the
processed start_url (id member, W3C).
scope is a separate URL. A document is within scope when its URL has the same origin as the
scope URL and its path starts with the scope URL’s path. If scope is missing, empty, invalid,
or does not contain start_url, the default is start_url with its filename, query, and fragment
removed (scope member, W3C).
Because matching uses a path prefix, "/sales" also contains /sales-report; use "/sales/"
when the intended boundary is that directory. Two application scopes can contain the same URL,
but scope does not identify an installed application. Use distinct, stable id values for
identity, and choose non-overlapping scope paths when each URL should remain within one
application context.
The cited BCD dataset records id support from Chrome 96, Safari 17 on macOS, and Safari
16.4 on iOS. Edge and Samsung Internet use mirrored data; that file lists no explicit versions
for them. Firefox parses the member without applying it (BCD manifests.webapp.id).
Examples
Section titled “Examples”Give sales and support applications separate identities and directory boundaries when serving them from the same origin.
Declaring the sales application
Section titled “Declaring the sales application”Link this manifest from documents that install the sales application.
{ "name": "Acme Sales", "id": "/apps/sales", "start_url": "/sales/", "scope": "/sales/", "display": "standalone"}The id is an identifier rather than a navigation target, so the specification does not require
it to be within scope.
Declaring the support application
Section titled “Declaring the support application”The support manifest uses another identity and a non-overlapping path prefix.
{ "name": "Acme Support", "id": "/apps/support", "start_url": "/support/", "scope": "/support/", "display": "standalone"}Keep the two id values stable when changing launch URLs or manifest URLs within the same origin.
Detecting the linked manifest with a fallback
Section titled “Detecting the linked manifest with a fallback”Checking the document does not reproduce the user agent’s id or scope processing. It detects
whether the browser reports support for the manifest link type and the current document declares
a manifest link. If either check fails, it retains ordinary website navigation.
The link’s relList is a DOMTokenList; its supports() method reports whether a token is supported
(DOMTokenList.supports(), MDN). The rel~= selector matches manifest within the space-separated rel tokens
defined by HTML.
const probe = document.createElement('link');const supportsManifestLink = typeof probe.relList?.supports === 'function' && probe.relList.supports('manifest');const manifestLink = document.querySelector('link[rel~="manifest"][href]');
if (!supportsManifestLink || !manifestLink) { document.documentElement.dataset.navigation = 'website';} else { document.documentElement.dataset.navigation = 'app-candidate'; console.info(`Linked manifest: ${manifestLink.href}`);}This check detects the document’s declaration only. It does not claim that the browser processes
id, that the application is installed, or that the current URL is within the processed scope.
See also
Section titled “See also”idmanifest memberscopemanifest member- The N+1 install problem
- Web Application Manifest:
idmember (W3C) - Web Application Manifest:
scopemember (W3C)
Specifications
| Specification | Status |
|---|---|
| manifest: id | W3C |
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Desktop)# | Yes | 96 | high | source | — |
| Chrome (Android)# | Yes | 96 | high | source | 1 |
| Edge (Desktop)# | Yes | 96 | high | source | 2 |
| Firefox (Desktop)# | No | — | high | source | 3 |
| Firefox (Android)# | No | — | high | source | 4 |
| Safari (macOS)# | Yes | 17 | high | source | — |
| Safari (iOS)# | Yes | 16.4 | high | source | — |
| Samsung Internet# | Yes | 17.0 | high | source | 5 |
| WebView (Android)# | Yes | 96 | high | source | 6 |
- Derived by browser-compat-data mirroring from Chrome.
- Derived by browser-compat-data mirroring from Chrome.
- The property parses, but has no effect.
- No Firefox for Android support is recorded in browser-compat-data.
- Derived by browser-compat-data mirroring from Chrome Android.
- Derived by browser-compat-data mirroring from Chrome Android.