← Chrome 152 reference

v152 · shipped · isolated web apps

Sub apps

Limited availability

  • Chrome · enabled by default 152 (developer trial in 150)
  • Edge · not separately reported
  • Firefox · no signal
  • Safari · no signal

Not on the Baseline register (no web-features entry at webstatus.dev). Chrome-only, and currently usable only on platforms that support Isolated Web Apps — presently ChromeOS (other desktops may work but are not officially supported). The Chrome 152 milestone listing (Enabled by default) and the current feature detail (chromestatus API, updated 2026-07-24) agree on desktop 152 as the default-on target, while the feature's status text remains “Proposed”.

Sub apps let a single Isolated Web App (IWA) installation present multiple distinct applications to the OS — each with its own name, icons, shelf window, and integrations such as protocol and file handlers — while sharing the parent IWA's origin, storage, permissions, Signed Web Bundle resources, and update lifecycle.

Ships in Chrome 152 — not reachable from ordinary websites

The Sub Apps API ships enabled by default in Chrome 152 (chromestatus milestone listing; it was in developer trial in Chrome 150). Even enabled by default, it is only exposed inside installed Isolated Web Apps. The documented setup (per the official sample) is:

Implementation/debug note: the runtime-enabled feature is SubApps (status: stable at trunk in runtime_enabled_features.json5), so --enable-blink-features=SubApps also gates the binding — but the flags-and-chrome://web-app-internals flow above is the documented complete setup.

Source: official subapps-demo README — how to try it; chromestatus.com/feature/6260680824061952

at a glance

At a glance
Milestone listingChrome 152 — Enabled by default (chromestatus listing, verified 2026-07-28; the feature was previously listed under Chrome 150 as In developer trial)
Default-on milestoneChrome 152, per two agreeing sources: the milestone listing (Enabled by default) and the feature detail (chromestatus API, updated 2026-07-24 12:57 UTC — shipping target desktop 152; active stage “Prepare to ship”, desktop_first 152). The feature's status text remains “Proposed”
InterfaceSubApps, reached via window.subApps
PlatformImplemented on desktop; end-user availability currently limited to platforms supporting Isolated Web Apps — presently ChromeOS (other desktops unofficial)
ContextInstalled Isolated Web Apps only (secure, isolated context)
Permission policysub-apps=(self) required on the parent IWA; policies declared for a sub app have no effect
Dev-trial flags#enable-isolated-web-apps, #enable-isolated-web-app-dev-mode, #enable-sub-apps, then install via chrome://web-app-internals
Runtime flag (impl. evidence)SubApps in runtime_enabled_features.json5 (status stable at trunk)
Standards bodyWICG — Sub Apps (unofficial draft)
ChromeStatus6260680824061952 — Sub apps
Source: chromestatus.com/feature/6260680824061952; Chromium runtime_enabled_features.json5

why it exists

An installed IWA can contain several applications, but a single launcher identity obscures them. The Sub Apps explainer's use cases include an office suite with separate email, document, and spreadsheet entry points that share one bundle and storage, and a virtual-desktop portal that gives streamed apps (such as a terminal and editor) their own launcher icons and windows. Sub apps provide those distinct OS entry points under one parent installation, instead of requiring separate heavyweight IWAs or one indistinguishable app window.

Source: WICG Sub Apps explainer — Use cases.

Syntax

The API hangs off Window as a single read-only attribute returning a SubApps instance with three asynchronous batch methods. IDL from the WICG draft:

[Exposed=Window, SecureContext, IsolatedContext]
partial interface Window {
  [SameObject] readonly attribute SubApps subApps;
};

// Represents the https://w3c.github.io/manifest/#id-member
typedef USVString ManifestId;

dictionary SubAppsAddResponse {
  record<USVString, ManifestId> installedApps;
  record<USVString, DOMException> failedApps;
};

dictionary SubAppsRemoveResponse {
  sequence<ManifestId> removedApps;
  record<USVString, DOMException> failedApps;
};

dictionary SubAppsListResult {
  required DOMString appName;
};

[Exposed=Window, SecureContext, IsolatedContext]
interface SubApps {
  Promise<SubAppsAddResponse> add(sequence<USVString> install_paths);
  Promise<SubAppsRemoveResponse> remove(sequence<ManifestId> manifest_ids);
  Promise<record<USVString, SubAppsListResult>> list();
};
Source: WICG Sub Apps draft — IDL index

add(install_paths)

Installs a batch of sub apps. Each install path is a relative URL (it must start with /) to the sub app's entry index.html, which must reference a web manifest. A single system prompt lists every requested sub app in one dialog (scrollable when batched).

add() — parameters, return value, exceptions
PartDetail
Parametersinstall_paths — sequence<USVString> of same-origin absolute paths to sub-app entry documents
ReturnsPromise<SubAppsAddResponse> — installedApps maps each install path to its new ManifestId; failedApps maps each failed path to a DOMException
Batch rejectionTypeError (invalid relative paths), SecurityError (sub-apps permissions policy missing), NotSupportedError (called inside a sub app), NotAllowedError (user declined the prompt), QuotaExceededError (platform limits exceeded), OperationError
Per-app failureConstraintError (scope prefix conflict, or path points at the parent's own manifest), DataError (manifest invalid/unparseable), InvalidStateError (already installed), OperationError

remove(manifest_ids)

Uninstalls a batch of previously installed sub apps, identified by the ManifestId values returned from add() or list().

remove() — parameters, return value, exceptions
PartDetail
Parametersmanifest_ids — sequence<ManifestId>
ReturnsPromise<SubAppsRemoveResponse> — removedApps lists the removed ManifestId values; failedApps maps each failed id to a DOMException
Batch rejectionTypeError, SecurityError, NotSupportedError, OperationError
Per-app failureNotFoundError (no such sub app under this parent IWA, or already removed), OperationError

list()

Returns the sub apps currently installed under this parent IWA as a record keyed by ManifestId, so dictionary lookups (e.g. membership checks) work directly on the result. Each value is a SubAppsListResult whose appName is the name from the sub app's web manifest.

list() — return value, exceptions
PartDetail
ReturnsPromise<record<USVString, SubAppsListResult>>
RejectionSecurityError (policy missing), NotSupportedError (called inside a sub app), OperationError
Source: WICG Sub Apps explainer — API design

Sub app identity, resources, and limits

A sub app is not a separate security principal. It shares the parent IWA's origin, SignedWebBundleId, cookies, IndexedDB, localStorage, Cache Storage, and all granted permissions — the same-origin policy treats parent and sub apps as one entity. What distinguishes a sub app is its presentation and OS integration:

Identity fields (per the explainer's identity table)
IdentifierRule
Origin / bundle idIdentical to the parent IWA
start_urlRequired in the sub app's manifest; must be unique across sub apps and the parent
ScopeSub-app scopes must not be prefixes of one another, nor of the parent scope — otherwise install/update fails with ConstraintError
ManifestIdThe manifest id member; falls back to start_url without the fragment. Used by remove() and list()

Sub-app manifests can carry their own OS integrations — protocol_handlers, file_handlers, and launch_handler act per sub app. Resources can be provisioned statically (bundled in the parent IWA, updated when the parent updates) or dynamically (a service worker serves the sub app's HTML and manifest, so sub apps update without a parent bundle update).

Two platform quotas guard against launcher flooding: at most 50 installed sub apps per parent IWA, and at most 20 sub apps per installation prompt. Exceeding either rejects the whole add() call with QuotaExceededError.

Source: WICG Sub Apps explainer — identity, provisioning, quota

Enterprise policies

Three Chrome enterprise policies govern whether sub-app installs require a user prompt:

Sub apps enterprise policies
PolicyEffect
DefaultSubAppsWithoutPromptsSettingGlobally sets whether IWA sub-app installs require a user permission prompt
SubAppsWithoutPromptsAllowedForOriginsLists IWA URLs that may add or update sub apps without a prompt
SubAppsWithoutPromptsBlockedForOriginsLists IWA URLs that may only add sub apps with a prompt
Source: chromestatus.com/feature/6260680824061952

Each policy is expanded below from the definition the Chrome Enterprise policy list publishes for it (policy ids 1448, 1449, 1450). Three facts are common to all three: they are defined for ChromeOS 150 and later only (supported_on: chrome_os:150-), they are per profile, and they support dynamic policy refresh, so a changed value applies without a browser restart. Precedence is fixed by the definitions: SubAppsWithoutPromptsBlockedForOrigins overrides SubAppsWithoutPromptsAllowedForOrigins, which overrides DefaultSubAppsWithoutPromptsSetting — so an origin listed in both lists is never prompt-free. The examples below are the managed-policy JSON form of each definition's own published example_value; the definitions publish a value domain and an example value but no per-platform deployment path, so none is claimed here.

DefaultSubAppsWithoutPromptsSetting

Caption from the definition: “Control use of the Sub Apps Add API”. This is the global default for whether an IWA origin may add sub apps at all, and whether the confirmation prompt described at add(install_paths) is shown.

DefaultSubAppsWithoutPromptsSetting — published definition (policy id 1448)
Value grammarInteger enum — published type int-enum, schema { "type": "integer", "enum": [1, 2, 3] }
Accepted values1 = AllowSubApps, “Allow Isolated Web App origins to add Sub Apps without user prompt”; 2 = BlockSubApps, “Block Isolated Web App origins from adding Sub Apps”; 3 = AskSubApps, “Allow Isolated Web App origins to add Sub Apps but require user prompt”
UnsetNo default value is published (default: null); the definition states that leaving the policy unset behaves as 3 — IWA origins may add sub apps upon user confirmation
Effect on add()At 3 or unset, the single system prompt documented at add(install_paths) is shown and declining it rejects that call with NotAllowedError. At 1, the definition says the origin may “bypass user gesture or user confirmation requirements”. At 2, IWA origins are blocked from adding sub apps
PrecedenceOverridden per origin by SubAppsWithoutPromptsAllowedForOrigins and by SubAppsWithoutPromptsBlockedForOrigins
Supported onChromeOS 150 and later. No other platform is listed under supported_on; chrome.* appears only under future_on, so Windows, macOS, Linux, Android and iOS support is not claimed for this policy
Refresh and scopeDynamic Policy Refresh (a change applies without a restart); Per Profile
Not specifiedThe definition publishes a closed enum and no behavior for a value outside [1, 2, 3], and it does not name the DOMException an add() call rejects with when 2 blocks the origin. Both are recorded as open questions in this page's critique rather than guessed
// Managed-policy JSON form of the published example_value (3)
{ "DefaultSubAppsWithoutPromptsSetting": 3 }   // AskSubApps  - prompt the user (same as unset)
{ "DefaultSubAppsWithoutPromptsSetting": 1 }   // AllowSubApps - prompt-free for every IWA origin
{ "DefaultSubAppsWithoutPromptsSetting": 2 }   // BlockSubApps - IWA origins cannot add sub apps

SubAppsWithoutPromptsAllowedForOrigins

Caption from the definition: “Allow Isolated Web Apps to use Sub Apps Add API without user prompt”. The definition states that by default IWA origins can add sub apps provided the user confirms the prompt interactively, and that this policy controls which IWA origins may add sub apps without the user being prompted.

SubAppsWithoutPromptsAllowedForOrigins — published definition (policy id 1449)
Value grammarArray of strings — published type list, schema { "type": "array", "items": { "type": "string" } }
Accepted valuesIWA origins. Both published example values use the isolated-app:// scheme (reproduced below). No wildcard or URL-pattern grammar is published
Effect on add()A listed IWA origin may add sub apps without the user being prompted; an origin that is not listed keeps the confirmation prompt documented at add(install_paths)
PrecedenceOverrides DefaultSubAppsWithoutPromptsSetting, and is itself overridden by SubAppsWithoutPromptsBlockedForOrigins — an origin present in both lists is not prompt-free
Supported onChromeOS 150 and later. No other platform is listed under supported_on; chrome.* appears only under future_on
Refresh and scopeDynamic Policy Refresh (a change applies without a restart); Per Profile
Not specifiedNo origin-matching rule beyond the string itself, no behavior for a malformed entry (an invalid origin, a wrong scheme, or an empty list), and no statement of whether the exemption also reaches a sub-app update — the definition speaks only of adding. Recorded as open questions in this page's critique rather than guessed
// Managed-policy JSON form of the definition's own published example_value
{
  "SubAppsWithoutPromptsAllowedForOrigins": [
    "isolated-app://egoxo6biqdjrk62rman4vvr5cbq2ozsyydig7jmdxcmohdob2ecaaaic",
    "isolated-app://anayaszofsyqapbofoli7ljxoxkp32qkothweire2o6t7xy6taz6oaacai"
  ]
}

SubAppsWithoutPromptsBlockedForOrigins

Caption from the definition: “Block Isolated Web Apps from using Sub Apps Add API without user prompt”. It controls which IWA origins are blocked from adding sub apps without the user being prompted interactively — the deciding policy wherever it overlaps the other two.

SubAppsWithoutPromptsBlockedForOrigins — published definition (policy id 1450)
Value grammarArray of strings — published type list, schema { "type": "array", "items": { "type": "string" } }
Accepted valuesIWA origins. Both published example values use the isolated-app:// scheme (reproduced below). No wildcard or URL-pattern grammar is published
Effect on add()A listed IWA origin may only add sub apps with the user prompted interactively, even where DefaultSubAppsWithoutPromptsSetting is 1 or the origin also appears in SubAppsWithoutPromptsAllowedForOrigins
PrecedenceThe definition states it “overrides the behavior of DefaultSubAppsWithoutPromptsSetting and SubAppsWithoutPromptsAllowedForOrigins” — the highest-priority of the three
Supported onChromeOS 150 and later. No other platform is listed under supported_on; chrome.* appears only under future_on
Refresh and scopeDynamic Policy Refresh (a change applies without a restart); Per Profile
Not specifiedNo origin-matching rule beyond the string itself and no behavior for a malformed entry (an invalid origin, a wrong scheme, or an empty list). Recorded as open questions in this page's critique rather than guessed
// Managed-policy JSON form of the definition's own published example_value
{
  "SubAppsWithoutPromptsBlockedForOrigins": [
    "isolated-app://egoxo6biqdjrk62rman4vvr5cbq2ozsyydig7jmdxcmohdob2ecaaaic",
    "isolated-app://anayaszofsyqapbofoli7ljxoxkp32qkothweire2o6t7xy6taz6oaacai"
  ]
}
Source: DefaultSubAppsWithoutPromptsSetting, SubAppsWithoutPromptsAllowedForOrigins and SubAppsWithoutPromptsBlockedForOrigins on the Chrome Enterprise policy list — captions, descriptions, value schemas, enum members, example values, supported_on, dynamic refresh and per-profile flags, and the precedence statements — as published in the policy definition data that list renders (policy ids 1448, 1449, 1450; fetched 2026-10-06).

Examples

Live example from the Chrome Platform Showcase: probes each prerequisite (secure context, IWA binding, permission policy) and reports exactly which gate is closed. Source: chrome-platform-showcase
// Inside an installed IWA with Permissions-Policy: sub-apps=(self)
const { installedApps, failedApps } = await window.subApps.add(["/calc/", "/docs/"]);

for (const [installPath, manifestId] of Object.entries(installedApps)) {
  console.log(`installed: ${installPath} -> ${manifestId}`);
}
for (const [installPath, exception] of Object.entries(failedApps)) {
  console.error(`failed: ${installPath} — ${exception.name}`);
}

// Later: enumerate and remove. The second id is deliberately not one of this
// parent IWA's sub apps, so the response shows BOTH of its members: removedApps
// for the ids it uninstalled, failedApps for the ids it could not (NotFoundError).
const all = await window.subApps.list();          // record<ManifestId, { appName }>
const calcId = Object.keys(all).find((id) => all[id].appName === "Calculator");
if (calcId) {
  const { removedApps, failedApps: notRemoved } =
    await window.subApps.remove([calcId, "stale-manifest-id"]);
  console.log("removed:", removedApps);
  for (const [manifestId, exception] of Object.entries(notRemoved)) {
    console.error(`not removed: ${manifestId} — ${exception.name}`);
  }
}
Source: WICG Sub Apps explainer — examples

Browser compatibility

There is no browser-compat-data (BCD) entry for this API yet, so the table below is an interim view built from ChromeStatus ship data and public vendor signals — not from BCD.

Interim compatibility — from ChromeStatus, not BCD
BrowserStatusNotes
Chrome150 (developer trial)Isolated Web Apps only — presently ChromeOS for end users. Default-on target desktop 152, per the agreeing v152 milestone listing (Enabled by default) and feature detail (API, 2026-07-24); status text “Proposed”
EdgeNot separately reportedNo Edge position is recorded on ChromeStatus; no public support evidence (Chromium engine equivalence is not a support commitment)
FirefoxNo signalRecorded on ChromeStatus
SafariNo signalRecorded on ChromeStatus
Source: chromestatus.com/feature/6260680824061952; mdn/browser-compat-data (no api/SubApps entry as of July 2026)

Specifications

Specifications
SpecificationStatus
Sub AppsWICG unofficial draft (incubation)
Web Application ManifestW3C Working Draft — sub apps reuse its id, start_url, scope, and OS-integration members
Source: spec links recorded on chromestatus.com/feature/6260680824061952

See also