# Add push notifications

> Subscribe a service worker to a push service, ask for notification permission at the right moment, and show the message when the push event arrives.

import { Steps } from '@astrojs/starlight/components';

**Outcome:** your server can reach the user with a message even when the app is not in
the foreground. Per MDN, the Push API gives web applications the ability to receive
messages pushed to them from a server, whether or not the web app is in the foreground —
or even currently loaded — and per MDN the service worker is started as necessary to
handle an incoming push message.

Two separate APIs are involved, and they are easy to confuse. Push is the transport: per
MDN, it delivers the message to the worker's `push` event. Notifications are the display:
per MDN, the worker reacts to the message by, for example, displaying a notification with
`ServiceWorkerRegistration.showNotification()`.

## Browser & ecosystem support

Per MDN, the Push API is Baseline "widely available" — well established across many
devices and browser versions, and available across browsers since March 2023. Per MDN,
`PushManager.subscribe()` is available only in secure contexts (HTTPS) in some or all
supporting browsers. `Notification.requestPermission()` is the weaker link: per MDN, that
static method is **not** Baseline, because it does not work in some of the most widely
used browsers.

## Steps

<Steps>

1. **Have an active service worker.** Per MDN, for an app to receive push messages it has
   to have an active service worker; once it is active, it can subscribe using
   `PushManager.subscribe()`. If you do not have one yet, start with
   [Getting started](/guides/getting-started/).

2. **Ask for permission from a user gesture.** Per MDN, `Notification.requestPermission()`
   returns a promise that fulfills with `granted`, `denied`, or `default`, and the request
   should be made in response to user interaction. Per MDN, when the value is `default`
   the user's decision is unknown and the application will act as if permission was
   denied.

3. **Subscribe, also from a user gesture.** Per MDN, `subscribe()` calls should be done in
   response to a user gesture such as clicking a button — not only as best practice, but
   because browsers are moving to disallow notifications not triggered by a user gesture,
   and MDN names Firefox as already doing this from version 72.

4. **Send the subscription to your server.** Per MDN, the resulting `PushSubscription`
   includes everything the application needs to send a push message: an endpoint and the
   encryption key needed for sending data.

5. **Handle `push` in the worker and show something.** Per MDN, incoming push messages are
   delivered to the `push` event handler, which can react by displaying a notification
   using `ServiceWorkerRegistration.showNotification()`.

</Steps>

## Feature detection and fallback

Per MDN, `ServiceWorkerRegistration.pushManager` is the entry point into using push
messaging and returns a reference to the `PushManager` interface — so test for that
interface before offering the subscribe UI at all:

```js
async function subscribeToPush(applicationServerKey) {
  if (!('PushManager' in window)) {
    // No push support: keep the subscribe UI hidden and fall back to whatever
    // in-app channel you already have (an inbox view, an email digest).
    return null;
  }

  const permission = await Notification.requestPermission();
  if (permission !== 'granted') return null;

  const registration = await navigator.serviceWorker.ready;
  return registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey,
  });
}
```

Per MDN, `applicationServerKey` is a Base64-encoded string or `ArrayBuffer` containing an
ECDSA P-256 public key that the push server uses to authenticate your application server;
if specified, all messages from your application server must use the VAPID authentication
scheme and include a JWT signed with the corresponding private key. Per MDN, that key is
**not** the same ECDH key used to encrypt the data.

In the worker, turn the message into a notification:

```js
self.addEventListener('push', (event) => {
  const body = event.data ? event.data.text() : 'You have a new message.';
  event.waitUntil(self.registration.showNotification('New message', { body }));
});
```

## Practical checklist

- **`userVisibleOnly: true` is effectively mandatory.** Per MDN, `applicationServerKey` is
  required in some browsers such as Chrome and Edge, and they will reject the promise if
  `userVisibleOnly` is not set to `true`. Plan for a visible notification per push rather
  than silent background delivery.
- **Treat the endpoint as a secret.** Per MDN, the subscription endpoint is a unique
  capability URL: knowledge of the endpoint is all that is necessary to send a message to
  your application, so it needs to be kept secret or other applications could push to your
  users.
- **Guard against CSRF when subscribing.** MDN warns explicitly that when implementing
  `PushManager` subscriptions it is vitally important to protect against CSRF/XSRF issues
  in your app.
- **Delivery is not unlimited everywhere.** Per MDN, waking a service worker to deliver a
  push message increases resource usage, particularly battery, and there is currently no
  standard mechanism for handling this: Firefox allows a limited quota of push messages
  per application (refreshed each time the site is visited, with messages that generate
  notifications exempt), while Chrome has no limits.
- **Subscriptions expire and change.** Per MDN, the `pushsubscriptionchange` event fires
  when a push subscription has been invalidated or is about to be — for example when the
  push service sets an expiration time — so treat a stored subscription as something that
  can stop being valid, and re-subscribe when it does.
- **Each subscription belongs to one worker.** Per MDN, each subscription is unique to a
  service worker.
- **A denial is explicit.** Per MDN, `denied` means the user has explicitly denied
  permission for the current origin to display system notifications, and MDN's own example
  notes that in that case you should be respectful and not bother the user any more.

## Where to go next

- [Web Push reference](/reference/notifications/web-push/) — the protocol and the
  subscription model in reference form.
- [Notifications API reference](/reference/notifications/notifications-api/) — the display
  half, including what `showNotification()` accepts.
- [Notification permissions](/reference/notifications/permissions/) — the permission model
  in detail.
- [Push on iOS Safari](/reference/notifications/ios-safari-push/) — a platform-specific
  entry on this site.

← Back to the [Guides](/guides/) overview.