# Declarative Web Push

> Declarative Web Push JSON, window.pushManager subscriptions, Safari support, service worker fallback, feature detection, and parser failures.

Declarative Web Push lets a browser create and display a notification from a standard JSON push message without involving a service worker. A service worker can still process the message, while the declarative notification provides fallback content if that processing does not produce a replacement.

## How it works

The Push API exposes `window.pushManager` for a window-scoped subscription. The `subscribe()` options are optional: `userVisibleOnly` defaults to `false`, and `applicationServerKey` defaults to `null`. A user agent can require `userVisibleOnly: true`, and a push service can require a non-null `applicationServerKey`.

A declarative message has a top-level `web_push` value of `8030` and a `notification` object. The Push API requires `title` and `navigate` strings inside that object. `navigate` supplies the URL opened when the user activates the notification. Notification options such as `body`, `lang`, `dir`, `tag`, `icon`, and `actions` can also appear when they have the types defined by the specification.

:::caution
For a window-scoped manager, the Push API rejects `subscribe()` with a `NotAllowedError` `DOMException` when its scope URL does not use HTTPS. The parser returns failure when the payload is not JSON, `web_push` is absent or is not exactly `8030`, `notification` is not an object, or `title` or `navigate` is absent or is not a string ([PushManager.subscribe()](https://w3c.github.io/push-api/#dom-pushmanager-subscribe), W3C; [Declarative push message parser](https://w3c.github.io/push-api/#declarative-push-message-parser), W3C).
:::

## Support

WebKit shipped Declarative Web Push for Home Screen web apps on iOS and iPadOS 18.4, then added it on macOS in Safari 18.5. WebKit documents the same JSON as a backwards-compatible payload: browsers with declarative support handle it directly, while other browsers can pass its `notification` object to `showNotification()` in a service worker ([Meet Declarative Web Push](https://webkit.org/blog/16535/meet-declarative-web-push/), WebKit).

## Examples

A deployment can prefer a window-scoped subscription while retaining a registration-scoped manager and `push` handler for the fallback path.

### Detecting a window-scoped push manager

Check for `Notification` and `window.pushManager` before using the declarative subscription path. The fallback obtains a service worker registration and checks whether it has a `pushManager`. If neither manager is found, it returns `null`.

```js
async function getPushManager() {
  if ('Notification' in window && 'pushManager' in window) {
    return window.pushManager;
  }

  if ('serviceWorker' in navigator) {
    const registration = await navigator.serviceWorker.getRegistration();
    if (registration && 'pushManager' in registration) {
      return registration.pushManager;
    }
  }

  return null;
}
```

Pass the selected manager to the same `subscribe()` flow your application uses to obtain and store a `PushSubscription`.

### Sending the standard JSON shape

Create the payload on the application server, then send the serialized JSON through the existing Web Push delivery path. The browser validates `navigate` as a string and resolves its URL when creating the notification. Activation navigates to that previously validated URL.

```js
const message = {
  web_push: 8030,
  notification: {
    title: 'Build completed',
    body: 'The production build is ready to review.',
    lang: 'en-US',
    dir: 'ltr',
    navigate: 'https://example.com/builds/482',
  },
};

const payload = JSON.stringify(message);
```

Send `payload` to the endpoint from the recipient's `PushSubscription`. The browser can display the declared title and body and use `navigate` for activation.

### Handling the same payload in a service worker

Keep a `push` handler for browsers that deliver the message to JavaScript. WebKit's migration example takes the title from `notification` and passes the remaining members to `showNotification()`.

```js
self.addEventListener('push', (event) => {
  event.waitUntil((async () => {
    const { notification } = event.data.json();
    const { title, ...options } = notification;
    await self.registration.showNotification(title, options);
  })());
});
```

With declarative support, the browser handles the standard payload. Without it, this handler displays the same notification data through the existing service worker path.

## See also

- [Push API: Declarative push message](https://w3c.github.io/push-api/#declarative-push-message) (W3C)
- [Meet Declarative Web Push](https://webkit.org/blog/16535/meet-declarative-web-push/) (WebKit)
- [Web Push and PushManager.subscribe()](/reference/notifications/web-push/)
- [Web Push on iOS and iPadOS](/reference/notifications/ios-safari-push/)
- [Notifications API](/reference/notifications/notifications-api/)