A lightweight, framework-agnostic library for handling web push notifications easily.
Web push notifications allow you to engage users even when they are not actively using your website. Pushmatic simplifies handling web push notifications.
This package ensures consistent behavior across all browsers and handles browser-specific differences. For example:
- Service Worker First: Pushmatic ensures that the service worker is registered before requesting notification permission, which is required in Edge.
- User-Generated Events: It encourages requesting permission inside a short-lived user-generated event (e.g., a button click) because browsers like Firefox and Safari enforce this for security and user experience reasons.
Pushmatic has been tested across all browsers that support web push notifications, ensuring reliability and seamless integration.
Web push notifications is actually pretty simple: when a user chooses to allow browser notifications from your website, Microsoft/Mozilla/Google create a REST endpoint somewhere in their clouds. You just need to issue a signed POST request to that endpoint with a certain payload to have notifications pushed to the userβs browser. You can send as many requests as you like, and itβs completely free.
Many developers get confused about this due to misleading articles suggesting that you need Firebase. However, since the Web Push protocol was standardized in 2016, all major browsers support it directly, eliminating the need for Firebase.
Good read on this topic: How to send web push notifications for free without Firebase
- π¬ Subscribe users to push notifications
- π Check push notification permission status
- β Unified API to handle everything in one function
- β‘ Promise-based API for easy integration
- π Framework-agnostic (works with vanilla JS, React, Vue, etc.)
- π Consistent behavior across all browsers
npm install pushmatic
-
Live Demo: https://pushmatic.vercel.app/
-
Live Demo Source Code: https://github.com/mhmdsalahsebai/pushmatic-website
To generate VAPID keys, run:
npx web-push generate-vapid-keys --json
Example output:
Public Key:
Your generated public key
Private Key:
Your generated private key
- The public key is used on the client-side and can be stored in an environment variable.
β οΈ Never store the private key in the client-side code! The private key must remain secret and should only be used on your server to send push notifications.
import Pushmatic from "pushmatic";
Pushmatic.initializePushNotifications("/sw.js", {
userVisibleOnly: true,
applicationServerKey: "YOUR_PUBLIC_VAPID_KEY",
})
.then((subscription) => {
console.log("Subscribed:", subscription);
// Send subscription to you server
})
.catch(console.error);
β
Description:
Registers a service worker, requests notification permission, and subscribes to push notifications in a single call.
β Parameters:
serviceWorkerUrl: string
β Path to the service worker file.options: PushSubscriptionOptionsInit
β Push subscription options (must includeuserVisibleOnly: true
and yourapplicationServerKey
).
β Returns:
Promise<PushSubscription>
- Resolves with the subscription data.
If you prefer more control, you can use the individual methods instead of initializePushNotifications()
.
β
Description:
Requests permission from the user to enable push notifications.
β Returns:
Promise<string>
- Resolves with"granted"
if permission is allowed.- Rejects with an error if permission is denied.
β Example:
Pushmatic.requestPermission()
.then((status) => console.log("Permission status:", status))
.catch((error) => console.error("Permission error:", error));
β
Description:
Registers a service worker with the given script URL.
β Parameters:
scriptURL: string
β The path to your service worker file (e.g.,"/sw.js"
).
β Returns:
Promise<ServiceWorkerRegistration>
- Resolves with the service worker registration.
β Example:
Pushmatic.registerServiceWorker("/sw.js")
.then((registration) =>
console.log("Service worker registered:", registration)
)
.catch((error) => console.error("Service worker error:", error));
β
Description:
Subscribes the user to push notifications using the given service worker registration.
β Parameters:
registration: ServiceWorkerRegistration
β The service worker registration object.options: PushSubscriptionOptionsInit
β Push subscription options (must includeuserVisibleOnly
andapplicationServerKey
).
β Returns:
Promise<PushSubscription>
- Resolves with the push subscription data.
β Example:
const options = {
userVisibleOnly: true,
applicationServerKey: "your-public-key-here",
};
Pushmatic.registerServiceWorker("/sw.js")
.then((registration) => Pushmatic.subscribeToPush(registration, options))
.then((subscription) => console.log("Push subscription:", subscription))
.catch((error) => console.error("Push subscription error:", error));
Your service worker file (e.g., sw.js
) should be placed at the root of your website so that it can control the entire domain. Example content for sw.js
:
self.addEventListener("push", function (event) {
const data = event.data.json();
self.registration.showNotification(data.title, {
body: data.body,
icon: "/icon.png",
});
});
π At this point, Pushmatic library has completed its role. The next sections serve as tutorial.
import { useState } from "react";
import Pushmatic from "pushmatic";
function PushButton() {
const [subscribed, setSubscribed] = useState(false);
function handleSubscribe() {
Pushmatic.initializePushNotifications("/sw.js", {
userVisibleOnly: true,
applicationServerKey: "YOUR_PUBLIC_VAPID_KEY",
})
.then((subscription) => {
console.log("Subscribed:", subscription);
setSubscribed(true);
sendSubscriptionToBackEnd(subscription);
})
.catch(console.error);
}
function sendSubscriptionToBackEnd(subscription) {
return fetch("/api/save-subscription/", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(subscription),
})
.then((response) => {
if (!response.ok) {
throw new Error("Bad status code from server.");
}
return response.json();
})
.then((responseData) => {
if (!(responseData.data && responseData.data.success)) {
throw new Error("Bad response from server.");
}
});
}
return (
<button onClick={handleSubscribe} disabled={subscribed}>
{subscribed ? "Subscribed" : "Enable Notifications"}
</button>
);
}
export default PushButton;
<button id="push-button">Enable Notifications</button>
<script type="module">
import Pushmatic from "pushmatic";
document.getElementById("push-button").addEventListener("click", () => {
Pushmatic.initializePushNotifications("/sw.js", {
userVisibleOnly: true,
applicationServerKey: "YOUR_PUBLIC_VAPID_KEY",
})
.then((subscription) => {
console.log("Subscribed:", subscription);
})
.catch(console.error);
});
</script>
To send notifications from your server, use the web-push
library.
npm install web-push
Your backend should have an endpoint to save user subscriptions somewhere (e.g., a database):
const express = require("express");
const app = express();
app.use(express.json());
const subscriptions = [];
app.post("/api/save-subscription/", (req, res) => {
const subscription = req.body;
subscriptions.push(subscription);
res.status(201).json({ data: { success: true } });
});
app.listen(3000, () => console.log("Server running on port 3000"));
const webpush = require("web-push");
const vapidKeys = {
publicKey: "Your generated public key",
privateKey: "Your generated private key",
};
webpush.setVapidDetails(
"mailto:your-email@example.com",
vapidKeys.publicKey,
vapidKeys.privateKey
);
const payload = JSON.stringify({
title: "Hello!",
body: "This is a push notification.",
});
subscriptions.forEach((subscription) => {
webpush
.sendNotification(subscription, payload)
.then(() => console.log("Push notification sent!"))
.catch(console.error);
});
π More details: Sending messages with Web Push libraries
Pushmatic uses Jest for unit testing. To run tests:
npm test
Contributions are welcome! Feel free to open issues or submit pull requests.
MIT License Β© Mohamed Salah