> ## Documentation Index
> Fetch the complete documentation index at: https://notifizz.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Web Push

> Browser push notifications delivered even when your product's tab is closed — self-hosted, no third-party push provider.

# Web push

Web push delivers system notifications to your users' devices — even when your product's tab is closed. Notifizz sends them under **your own identity** (a per-environment VAPID key pair), directly through the browsers' push services. There is no third-party push provider in the loop, and the payload is end-to-end encrypted: the push services relay bytes they cannot read.

<Warning>
  **Closed beta.** Web push is enabled per organisation and is off by default. If the push channel doesn't appear when you add a notification to a campaign, ask your Notifizz contact to switch it on. Campaigns that already carry a push step keep sending either way.
</Warning>

## TL;DR

* **Two setup steps.** Generate the environment's key in *Settings → Web push*, then serve one file — `notifizz-sw.js` — at the root of your application's origin. The widget does the rest.
* **Opt-in is the user's.** The Notification Center widget shows a discreet, dismissible prompt — or drive the opt-in from your own button with the SDK, no bell required. Either way, the native browser permission dialog only ever fires on the user's explicit click.
* **Same identity as the Notification Center.** Recipients are matched on the user id your widget authenticates with — no separate audience to manage.
* **Verified from the real browser.** The widget checks in situ that the service worker is actually served on your origin and reports the result per environment — the settings page tells you exactly what's wrong, with the fix.
* **Privacy friendly.** Payloads are encrypted end to end (RFC 8291); browser push services never see the content. Notifizz holds no customer data beyond the subscription itself.

## How it works

1. **Set up the environment.** In *Settings → Web push*, generate the environment's key pair. The public key is served to your frontend automatically; the private key never leaves the Notifizz backend.
2. **Serve the service worker.** Download `notifizz-sw.js` — there's a **Download** button right under this step in *Settings → Web push* — and serve it at the root of your application's origin (`https://your-app.com/notifizz-sw.js`). It's a static file: no build step, no code. It registers under a dedicated scope, so it never conflicts with your own service worker or PWA.
3. **Let the widget do the rest.** If you already run the Notification Center widget, web push is live: at load time the widget verifies the service worker, reports the setup state, and — once a user opts in — registers the browser's push subscription with Notifizz.

```html theme={null}
<!-- Nothing new to install: the Notification Center widget carries web push. -->
<!-- Just make sure /notifizz-sw.js is served at your origin's root. -->
```

## The opt-in experience

Browsers punish unsolicited permission prompts — some permanently demote sites that ask on page load. Notifizz never does that:

* A **soft prompt** appears at the top of the notification panel, only when the environment is configured and the user hasn't decided yet.
* The native browser dialog fires **only on the user's click** of the "Enable" button.
* "Later" dismisses the prompt and remembers the choice per browser.
* Opting out from the widget removes the subscription on both sides.

A user can have several active subscriptions (one per browser and device) — each delivers independently, and dead ones are pruned automatically.

### Opt-in from your own UI

Don't run the notification center, or want the prompt somewhere the bell isn't? The SDK exposes the opt-in directly, so you own the button and its timing (a contextual prompt after real engagement converts far better than one on page load). The plumbing is identical — same service worker, same subscription — only the trigger is yours.

The status is reactive. It starts `unavailable` and settles once the widget has booted web push for the signed-in user:

| Status        | Meaning                                                | Your button                           |
| ------------- | ------------------------------------------------------ | ------------------------------------- |
| `unavailable` | Env has no key, or the service worker isn't served yet | Hide it                               |
| `unsupported` | The browser can't do web push                          | Hide it                               |
| `ready`       | Configured and verified, user hasn't decided           | **Enable it**                         |
| `subscribed`  | Already on                                             | Show "on" / offer opt-out             |
| `denied`      | User blocked the browser permission                    | Hide it (only the user can undo this) |

<CodeGroup>
  ```tsx React theme={null}
  import { useNotifizz } from "@notifizz/react";

  // Anywhere inside your <NotifizzProvider>
  function EnableNotifications() {
    const { webpush } = useNotifizz();

    if (webpush.status === "subscribed") {
      return <button onClick={webpush.optOut}>Turn off notifications</button>;
    }
    return (
      <button onClick={webpush.optIn} disabled={webpush.status !== "ready"}>
        Enable notifications
      </button>
    );
  }
  ```

  ```ts Angular theme={null}
  import { Component } from "@angular/core";
  import { NotifizzService } from "@notifizz/angular";

  @Component({
    selector: "enable-notifications",
    template: `
      <button
        (click)="notifizz.webpushOptIn()"
        [disabled]="(notifizz.webpushStatus$ | async) !== 'ready'"
      >
        Enable notifications
      </button>
    `,
  })
  export class EnableNotificationsComponent {
    constructor(public notifizz: NotifizzService) {}
  }
  ```

  ```ts Vanilla theme={null}
  import { createNotifizz } from "@notifizz/vanilla";

  const notifizz = createNotifizz({
    apiKey,
    authType: "backendToken",
    userId,
    userEmail,
    token,
  });
  notifizz.mount();

  button.addEventListener("click", () => notifizz.webpushOptIn());

  // Keep the button in sync with the reactive status.
  notifizz.onStateChange((s) => {
    button.disabled = s.webpushStatus !== "ready";
  });
  ```
</CodeGroup>

Call `optIn()` from a real user gesture (a click) — the browser ignores a permission request that isn't tied to one.

<Note>
  On a framework Notifizz doesn't ship a SDK for (Vue, Svelte, Solid…)? The same opt-in is two bridge commands — `window.notifizz("webpushOptIn")` and the reactive `webpushStatus` on the `notifizz:state` event. See the [widget bridge reference](/docs/sdks/notification-center/widget-bridge#web-push-opt-in).
</Note>

## Sending

Web push is a channel like any other: pick it when adding a notification to a campaign, compose the title and body (with variables), and the campaign copilot can design it for you. Two guardrails are specific to the channel:

* **Review never reaches real users.** Before a campaign is Live, pushes are delivered only to your team's *sandbox testers* — members who opted in from *Settings → Web push* ("Test on this browser", capped at 5).
* **Payload budget.** Push services cap payloads around 4 KB; the composer keeps you within it, and titles/bodies are truncated aggressively by the OS (the composer's platform preview shows exactly how).

## FAQ

<AccordionGroup>
  <Accordion title="Do my users need to install anything?">
    No. Web push works in the browser they already use. The only requirement on your side is serving the `notifizz-sw.js` file on your origin — a static file, no code changes.
  </Accordion>

  <Accordion title="Which users can receive a push?">
    Users who granted the browser permission through the widget's opt-in and have at least one active subscription. Recipients are matched on the same user id as the Notification Center.
  </Accordion>

  <Accordion title="What happens if a user clears their browser data?">
    Their subscription dies. The push services report it on the next send, and Notifizz prunes that subscription automatically — the user simply opts in again next time.
  </Accordion>

  <Accordion title="Can I use my own push provider?">
    Notifizz sends through the browsers' push services directly under your VAPID identity — there is no per-send cost and no provider account to create. Provider-swapping per organisation is supported by the architecture if a specific need arises; talk to us.
  </Accordion>
</AccordionGroup>
