# handle_links manifest member

> handle_links is a WICG proposal letting an installed PWA ask to open in-scope links in its own window. No engine ships it; Chrome 155 drops the member silently.

`handle_links` is a proposed manifest member, written up in the WICG `pwa-url-handler`
explainer, through which an installed PWA would tell the browser whether links into its `scope`
should open inside the installed app window instead of a browser tab. The member takes one of
three string values and is a hint, not a guarantee: the user agent stays free to honour a user
preference over the manifest.

No engine ships it. The Chromium entry on chromestatus.com lists the implementation as "On hold"
with no origin trial and no milestone, and Firefox and Safari have published no position
(chromestatus.com, feature 5740751225880576). Chrome 155 parses the manifest without it: the
member is dropped silently like any unknown key. Link capturing exists in Chromium anyway, but as
a per-app user setting rather than a manifest declaration.

## Member

- **Type**: string, one of `"auto"`, `"preferred"`, or `"not-preferred"`.
- **Default**: `"auto"` when the member is absent or holds another value; the user agent
  picks the platform's own behaviour.
- **Example value**: `"preferred"`.

`preferred` asks the user agent to open in-scope links in the installed app; `not-preferred`
asks it to keep them in the browser; `auto` defers to the platform. The explainer's non-goals
bound what the member covers: it does not widen `scope` (that is `scope_extensions`), and it
does not decide what happens inside the app once a link lands (that is `launch_handler`). The
only question it answers is whether an in-scope link reaches the app at all.

What shipped instead decides the behaviour in practice. Desktop Chrome 155 and Edge 154 offer a
per-app toggle in the app's settings page (`chrome://apps`, right-click, **App settings**) named
**Opening supported links** with the choice **Open in [app name]**; the default is off and the
manifest cannot flip it. Chrome for Android captures links into a WebAPK when the site's
`assetlinks.json` verifies the app through Digital Asset Links, again with no manifest input.

:::observed
Chrome 155 on macOS 26 (English UI): with `"handle_links": "preferred"` added to an otherwise
valid manifest, DevTools > Application > Manifest shows no section for the member and no
**Errors and warnings** entry, and the Console prints nothing. Unknown members are dropped
without a diagnostic, so the only way to notice that `handle_links` had no effect is to click
an in-scope link from another app and watch it open in a browser tab.
:::

## Examples

The first example shows the proposed syntax; the second shows the member that does work in
Chromium for routing a captured link once it reaches the app.

### Declaring the preference the explainer describes

The member sits at the top level next to `scope`. `start_url` must remain inside `scope` for the
declared boundary to apply; otherwise the user agent falls back to a scope derived from
`start_url` (w3.org, Web Application Manifest "scope member") and the links `handle_links` would
cover are not the ones the author intended.

```json
{
  "name": "Tracker",
  "start_url": "/app/",
  "scope": "/app/",
  "handle_links": "preferred"
}
```

This manifest is valid everywhere because unknown members are ignored. On Chrome 155 the line
changes nothing; it documents intent for a browser that may implement the explainer later.

### Routing a captured link with launch_handler and launchQueue

When the user has enabled link capturing for the app, Chrome 110+ launches the installed app
and reports the clicked URL through `LaunchParams.targetURL`. Pairing `launch_handler` with a
`launchQueue` consumer keeps a single window and navigates it in place; without a consumer the
browser opens the URL in a new app window.

```json
{
  "launch_handler": { "client_mode": "navigate-existing" }
}
```

```js
if ('launchQueue' in window) {
  window.launchQueue.setConsumer((launchParams) => {
    if (!launchParams.targetURL) return; // Icon launch, nothing to route.
    const url = new URL(launchParams.targetURL);
    router.navigate(url.pathname + url.search);
  });
} else {
  // Firefox 157 and Safari 27: links open in a tab; the page loads the URL normally.
}
```

`client_mode: "navigate-existing"` is what makes the captured link reuse the open window; the
`launchQueue` consumer is only needed for client-side routing. A page that renders from the
URL on load needs neither.

## See also

- [scope manifest member](/reference/manifest/scope/)
- [scope_extensions manifest member](/reference/manifest/scope-extensions/)
- [launch_handler manifest member](/reference/manifest/launch-handler/)
- [WebAPK: how Chrome installs PWAs on Android](/reference/installation/webapk/)
- [PWA URL Handler: handle_links explainer](https://github.com/WICG/pwa-url-handler/blob/main/handle_links/explainer.md) (github.com)
- [Chrome Platform Status: Web App Link Handling (handle_links)](https://chromestatus.com/feature/5740751225880576) (chromestatus.com)
- [Launch Handler API](https://developer.chrome.com/docs/web-platform/launch-handler) (developer.chrome.com)