# Manifest protocol_handlers: registering a PWA for custom URL schemes

> How the manifest's protocol_handlers member registers an installed PWA to handle clicks on a given URL scheme, the web+ naming rule for custom schemes, and the %s placeholder that carries the clicked URL into your app.

import CompatTable from '@components/CompatTable.astro';

**In one line:** the manifest's `protocol_handlers` member registers an installed PWA as
the OS's handler for a URL scheme (like `mailto:` or a custom `web+`-prefixed one), so
clicking a matching link — from a browser tab, another app, or the OS — launches your app
with that URL instead of just navigating a page.

## What it is

Per MDN, `protocol_handlers` is an array of objects, each with a required `protocol`
(the scheme to handle, e.g. `mailto`, `ms-word`, or `web+jngl`) and a required `url` (an
HTTPS URL within the app's scope where the `%s` token is replaced with the full clicked
URL). Registering the association with the OS usually happens at install time, though per MDN
it can also be made afterward for an already-installed app.

## Where it is supported

<CompatTable feature="protocol-handlers" />

Manifest-based `protocol_handlers` registration is desktop-only across the browsers that
implement it; there is also the separate, older `navigator.registerProtocolHandler()` API
for registering a handler imperatively from a page, which does not require installation.

## How to use it

Declare the protocol and the URL template in the manifest:

```json
{
  "protocol_handlers": [
    {
      "protocol": "web+coffee",
      "url": "/coffee?type=%s"
    }
  ]
}
```

Separately, MDN documents a naming rule for the related, older
`navigator.registerProtocolHandler()` API: its `scheme` argument must begin with `web+`
followed by one or more lower-case ASCII letters (e.g. `web+coffee`), or be one of a fixed
safelist such as `mailto`, `bitcoin`, or `magnet`. The manifest's `protocol_handlers` page
itself does not document a naming restriction on its `protocol` field.

Once installed and registered, the `%s` placeholder in the handler URL is replaced by
the URL that starts with the handler's scheme.

## How to detect it at runtime

Per MDN, associating a protocol with an installed app is usually done during installation,
though the association can also be made afterward for an already-installed app. What a
page can detect is whether the separate, imperative `registerProtocolHandler()` method
exists, for sites that want to offer registration outside of installation:

```js
if ('registerProtocolHandler' in navigator) {
  navigator.registerProtocolHandler('web+coffee', '/coffee?type=%s');
} else {
  // No imperative registration API — rely on the manifest-declared
  // protocol_handlers association completed at install time instead.
}
```

## What goes wrong

- [ ] Manifest-based `protocol_handlers` registration only takes effect once the app is
      **installed** — it does not register anything for a page opened in a plain browser
      tab.
- [ ] MDN's documented `web+`-prefix / safelist naming rule applies to the older
      `registerProtocolHandler()` API's `scheme` argument — its manifest `protocol_handlers`
      page does not itself state that `protocol` values are restricted the same way.
- [ ] The `url` member must be within the app's manifest scope and use HTTPS, per MDN —
      it cannot point off-origin.
- [ ] The `%s` placeholder is replaced with the entire clicked URL, not just a parsed
      payload — parse out the part after the scheme yourself in the handler route.

## Where to go next

- [manifest: file_handlers support](/compatibility/manifest-file-handlers/) — another
  manifest member that registers an installed PWA with the OS, for file types instead of
  URL schemes.
- [Handle files](/guides/file-handling/) — a related guide to registering an installed PWA
  for OS-level file associations.