Push Notifications
Subscribe, notify, unsubscribe, and framework adapters for push notification management.
If you've worked with the Web Push API directly, Swoff provides the browser-side subscription management and SW push event handler — you just need a server endpoint to send the subscription and trigger pushes.
Preconditions
- Swoff initialized with a registered service worker
- A server endpoint to store subscriptions and send push events (VAPID keys required)
Status
Disabled by default. Push notifications must be explicitly enabled.
Enable
npx @swoff/cli add push-notificationOr set features.pushNotifications: true in swoff.config.json then regenerate.
Generated files
| File | What it does | Import in your code? |
|---|---|---|
swoff/push-notification/index.ts | requestNotificationPermission(), subscribeToPush(), unsubscribeFromPush(), isSubscribed(), getPushSubscription() | Yes (bundler only) |
swoff/sw/template.js | Push event handler — receives push events, displays notifications | No (built into SW) |
swoff-api.bundle.js | Same push API via window.swoff.subscribeToPush() etc. for no-bundler projects | No (auto-initializes) |
Usage
Bundler projects
import {
requestNotificationPermission,
subscribeToPush,
unsubscribeFromPush,
isSubscribed,
getPushSubscription,
} from "./swoff/push-notification";
// Step 1: request permission (user gesture required)
const granted = await requestNotificationPermission();
if (!granted) return;
// Step 2: subscribe — returns PushSubscription
const subscription = await subscribeToPush();
// Send subscription to your server:
await fetch("/api/push/subscribe", {
method: "POST",
body: JSON.stringify(subscription.toJSON()),
});
// Step 3: check status
const sub = await getPushSubscription();
const subscribed = await isSubscribed();
// Step 4: unsubscribe
await unsubscribeFromPush();
await fetch("/api/push/unsubscribe", { method: "POST" });No-bundler projects
Include the auto-initializing bundles, then call the API on window.swoff:
<script src="/swoff/client-injector.bundle.js"></script>
<script src="/swoff/swoff-api.bundle.js"></script>
<script>
swoff.configure({ push: { vapidPublicKey: "YOUR_VAPID_PUBLIC_KEY" } });
const granted = await swoff.requestNotificationPermission();
if (!granted) return;
const subscription = await swoff.subscribeToPush();
await fetch("/api/push/subscribe", {
method: "POST",
body: JSON.stringify(subscription.toJSON()),
});
const subscribed = await swoff.isSubscribed();
await swoff.unsubscribeFromPush();
</script>Framework adapters
This section has one adapter:
useSwoffPush
Exposes push state: { subscribed, permission, subscribe, unsubscribe }.
Events listened to:
| Event | Detail shape | Purpose |
|---|---|---|
push-subscription-changed | { subscribed } | Push subscription added or removed |
push-permission-changed | { permission } | Notification permission granted or denied |
If your framework already has a useSwoffPush adapter (React, Vue, Svelte), the CLI generates it into swoff/adapters/. To create adapters for any other framework, see the Blueprint for the event reference and adapter patterns.
Server side (example)
The generated SW handles incoming push events and displays notifications. Your server sends push messages via the Web Push protocol:
// Server code (any language)
await webpush.sendNotification(
subscription,
JSON.stringify({
title: "New Note",
body: "Someone shared a note with you",
icon: "/icon-192.png",
data: { url: "/notes/123" },
}),
);Customize
The push event handler is embedded in the generated SW (swoff/sw/template.js). It parses the push payload and calls self.registration.showNotification(). To customize notification appearance or behavior, edit the push event section in the template.
Config
{
"features": {
"pushNotifications": true
}
}pushNotifications— enable push subscription management and SW push handler
The VAPID public key is set at runtime via swoff.configure({ push: { vapidPublicKey: "..." } }) for no-bundler projects, or directly in swoff/push-notification/index.ts for bundler projects. Generate a key pair with npx web-push generate-vapid-keys.