JJopgood PWAcore + adaptersv0.0.2GitHub

Quick Start

Register a service worker, request notification permission, and subscribe a user, without any framework.

A working push subscription, without a framework or adapter.

Steps

Install the package

npm install @jopgood/pwa-core

Create a service worker

Add public/sw.js with at minimum a push handler. The core registers this file; it doesn't generate it. See the Service Worker API on MDN ↗ for a full walkthrough.

self.addEventListener("push", (event) => {
  const data = event.data?.json() ?? {};
  event.waitUntil(
    self.registration.showNotification(data.title ?? "Notification", {
      body: data.body,
    }),
  );
});

Construct the manager and mount

Construction is safe to run on the server. mount() is what touches the browser, so call it after hydration.

import { PWAManager } from "@jopgood/pwa-core";

const manager = new PWAManager({
  serviceWorkerUrl: "/sw.js",
  vapidPublicKey: "YOUR_VAPID_PUBLIC_KEY",
  onSubscriptionChange: (subscription) => {
    if (subscription) {
      fetch("/api/push/register", {
        method: "POST",
        body: JSON.stringify(subscription),
      });
    }
  },
});

manager.mount();

Request permission, then subscribe

subscribe() requires permission === "granted". Request it from a user gesture (click) so the browser will show the prompt.

document.querySelector("#enable-push").addEventListener("click", async () => {
  const permission = await manager.requestNotificationPermission();
  if (permission === "granted") {
    await manager.subscribe();
  }
});

Verify in the browser

Open DevTools → Application → Service Workers (worker should be activated) and Application → Push messaging (subscription should be listed).

Reading state without a framework

The manager exposes its store directly. Subscribe to it the same way an adapter would:

const unsubscribe = manager.store.subscribe(() => {
  const { isSubscribed, permission, swUpdateAvailable } = manager.store.state;
  // update your UI
});

Handling SW updates

When a new service worker installs, state.swUpdateAvailable flips to true. Prompt the user, then activate:

if (manager.store.state.swUpdateAvailable) {
  manager.activateWaiting();
}

The page reloads once the new worker takes control.

Tear it down

manager.unmount();

Removes the internal listeners. Useful in tests and HMR.