Web SDK
Real-time push notifications for browsers via WebSocket + Web Push (VAPID). No Firebase dependency. Works with React, Vue, Angular, vanilla JS.
A persistent connection over TLS (port 443) delivers messages instantly while the tab is open
Background notifications when browser is closed via service worker
Images, action buttons, badges, deep links
Broadcast to interest groups
Exponential backoff handles flaky networks
Unified badge count across the platforms that support it
Notification title/body localizations picked by the browser language
React, Vue, Angular, Svelte, vanilla JS
Installation#
The SDK installs from Abrova Center in two steps.
- Point the
@abrovascope at Abrova Center in your project's.npmrc:
# .npmrc (in your project root) @abrova:registry=https://center.abrova.ir/api/packages/abrova/npm/
- Install the package:
npm install @abrova/push-web@^0.1.12
Without a bundler
The package also ships a ready-made browser build. Copy it into your public directory and load it from your own site:
cp node_modules/@abrova/push-web/dist/index.umd.js public/abrova-push.umd.js
<script src="/abrova-push.umd.js"></script>
Service Worker Setup#
The Web SDK uses a service worker for background notifications (Web Push). Copy it once to your public directory:
cp node_modules/@abrova/push-web/service-worker.js public/abrova-push-sw.js
Web Push requires HTTPS. localhost works for development. Make sure the service worker file is served from the root of your public directory.
Copy service-worker.js again whenever you upgrade @abrova/push-web. An older worker keeps working, but misses fixes such as SDK version reporting on re-subscription.
Quick Start#
import AbrovaPush from '@abrova/push-web';
const abrovaPush = new AbrovaPush({
apiKey: 'ab_live_your_api_key',
});
abrovaPush.onMessage((message) => {
// message.title, message.body, message.data
});
abrovaPush.onConnectionState((state) => {
// 'connecting' | 'connected' | 'disconnected' | 'error'
});
abrovaPush.onRegistered((deviceId) => {
// Device registered with the Abrova backend
});
// Register the device — this also requests notification permission.
// Once permission is granted, call register() on every page load to keep the
// device's subscription, app version and last-seen time current.
const deviceId = await abrovaPush.register({ userId: 'user_123' });Callbacks#
All event handlers return an unsubscribe function. Call it on teardown.
// Foreground messages
const unsub = abrovaPush.onMessage((message) => {
// message.title, message.body
});
// Connection state changes
abrovaPush.onConnectionState((state) => {
// 'connecting' | 'connected' | 'disconnected' | 'error'
});
// Notification clicked while the browser was in background
abrovaPush.onNotificationClick((message, action) => {
if (message.deepLink) {
window.location.href = message.deepLink;
}
});
// Action button clicked on a notification
abrovaPush.onActionClicked((actionId, message) => {
// actionId from the original push payload
});
// Errors
abrovaPush.onError((error) => {});
abrovaPush.onDetailedError((error) => {
// error.code, error.message
});
// Reconnection attempts
abrovaPush.onReconnecting((state) => {
// state.retryAttempt, state.nextRetryMs
});
// Network state changes
abrovaPush.onNetworkState((state) => {
// state.isAvailable, state.networkType
});
// Tab visibility changes
abrovaPush.onAppState((state) => {
// state.isVisible
});
// Clean up
unsub();User Identification#
// Set user ID after login
await abrovaPush.setUserId('user_123');
// Clear user ID on logout — also detaches the user on the server,
// so this browser stops receiving the previous user's notifications
await abrovaPush.clearUserId();
// Register with user ID in one call
await abrovaPush.register({ userId: 'user_123' });Topics#
await abrovaPush.subscribeTopic('news');
await abrovaPush.subscribeTopic('promotions');
await abrovaPush.unsubscribeTopic('promotions');Badge Management#
Unified badge API — uses the native Badge API where available and falls back to drawing on the favicon.
abrovaPush.setBadgeCount(5); abrovaPush.clearBadge(); const count = abrovaPush.getBadgeCount();
Message Inbox#
Server-stored messages users can revisit, with live updates and an offline cache (0.1.6+). See the Inbox guide.
abrovaPush.inbox.onMessage((message) => render(message));
abrovaPush.inbox.onUnreadCountChange((count) => setBadge(count));
const { messages, unreadCount } = await abrovaPush.inbox.getMessages({ status: 'unread' });
await abrovaPush.inbox.markAsRead(messages[0].id);
await abrovaPush.inbox.markAllAsRead();Analytics#
Forward SDK events to your analytics service:
abrovaPush.setAnalyticsHandler((event, properties) => {
analytics.track(`abrova_push_${event}`, properties);
});
// Disable if needed (e.g. for GDPR)
abrovaPush.disableAnalytics();Log Levels#
import { AbrovaPushLogLevel } from '@abrova/push-web';
abrovaPush.setLogLevel(AbrovaPushLogLevel.DEBUG); // Development
abrovaPush.setLogLevel(AbrovaPushLogLevel.ERROR); // ProductionAvailable levels: NONE, ERROR, WARNING, INFO, DEBUG, VERBOSE.
Utilities#
const connected = abrovaPush.isConnected(); const deviceId = abrovaPush.getDeviceId(); const network = abrovaPush.getNetworkState(); const appState = abrovaPush.getAppState(); const initialMessage = abrovaPush.getInitialMessage(); // Static checks AbrovaPush.isSupported(); AbrovaPush.getPermissionStatus(); // Unregister the device await abrovaPush.unregister();
Configuration Options#
const abrovaPush = new AbrovaPush({
apiKey: 'ab_live_your_api_key',
serviceWorkerPath: '/abrova-push-sw.js',
autoRegisterServiceWorker: true,
maxReconnectAttempts: 0,
logLevel: AbrovaPushLogLevel.ERROR,
appVersion: '2.0.0',
autoRefresh: true,
});| Option | Type | Required | Description |
|---|---|---|---|
| apiKey | string | Yes | Your project API key (`ab_live_...`) from Abrova Console |
| serviceWorkerPath | string | No | Path to the service worker file (default: "/abrova-push-sw.js") |
| autoRegisterServiceWorker | boolean | No | Auto-register the service worker (default: true) |
| maxReconnectAttempts | number | No | Max reconnect attempts after the connection drops; 0 = no limit, backing off up to 60 s (default: 0) |
| logLevel | AbrovaPushLogLevel | No | NONE / ERROR / WARNING / INFO / DEBUG / VERBOSE (default: ERROR) |
| appVersion | string | No | Your web app version, shown per device and usable as a segment filter |
| vapidPublicKey | string | No | Fallback VAPID public key for Web Push; the key fetched from the server takes precedence, so normally leave unset |
| autoRefresh | boolean | No | On page load, silently re-register a browser that registered before, at most once every 24h or sooner when the push subscription, appVersion, SDK version or user changed. Never prompts for permission (default: true) |
| inApp | InAppConfig | No | In-app message options: enabled, display ('auto' | 'manual'), autoTrigger, bannerPosition, locale. The in-app API is on `abrovaPush.inApp` |
The installed SDK version is exported as SDK_VERSION:
import { SDK_VERSION } from '@abrova/push-web';Delivery Tracking
Notifications are confirmed as delivered automatically: Web Push arrivals by the service worker, and messages received over the real-time connection while the page is open by the SDK. No code needed. See Delivery Receipts.
Usage Without a Bundler#
For sites that don't use a bundler, load the browser build you copied in step 1:
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<script src="/abrova-push.umd.js"></script>
<script>
const abrovaPush = new AbrovaPush.default({
apiKey: 'ab_live_your_api_key',
});
abrovaPush.onMessage(function(message) {
// message.title, message.body
});
abrovaPush.register().then(function(deviceId) {
// Device registered
});
</script>
</body>
</html>API Reference#
AbrovaPush — Initialization & Lifecycle
| Option | Type | Required | Description |
|---|---|---|---|
| constructor(config) | - | - | Create the SDK instance |
| register(options?) | - | - | Register the device + request notification permission. Call on every page load once permission is granted |
| unregister() | - | - | Unregister the device |
| isConnected() | - | - | Check whether the WebSocket connection is open |
| getDeviceId() | - | - | Get the current device identifier |
| getNetworkState() | - | - | Get the current network availability + type |
| getAppState() | - | - | Get the current tab visibility state |
| getInitialMessage() | - | - | Get the message that opened the page from a notification tap |
abrovaPush.inbox — Message Inbox (0.1.6+)
| Option | Type | Required | Description |
|---|---|---|---|
| getMessages(filter?) | - | - | Fetch messages (status, category, limit, offset, locale) |
| getMessage(id) | - | - | Fetch one message |
| getCachedMessages() | - | - | Cached messages, no network |
| getUnreadCount() | - | - | Unread count from the cache |
| fetchUnreadCount() | - | - | Unread count from the server |
| markAsRead(id) | - | - | Mark one message read |
| archiveMessage(id) | - | - | Archive one message |
| deleteMessage(id) | - | - | Delete one message |
| markMultiple(ids, status) | - | - | Set status on several messages ('read' | 'archived' | 'deleted') |
| markAllAsRead() | - | - | Mark every message read |
| clearCache() | - | - | Drop the local cache |
| onMessage(cb) | - | - | New inbox message; returns an unsubscribe function |
| onStatusChange(cb) | - | - | A message status changed |
| onUnreadCountChange(cb) | - | - | Unread count changed — use it for a badge |
Static methods
| Option | Type | Required | Description |
|---|---|---|---|
| AbrovaPush.isSupported() | - | - | Check whether the browser supports notifications + service workers |
| AbrovaPush.getPermissionStatus() | - | - | Current Notification.permission value |
Callbacks
All callbacks return an unsubscribe function.
| Option | Type | Required | Description |
|---|---|---|---|
| onMessage(callback) | - | - | Foreground push received |
| onConnectionState(callback) | - | - | WebSocket connection state changed |
| onRegistered(callback) | - | - | Device registration completed |
| onNotificationClick(callback) | - | - | User clicked a background notification |
| onActionClicked(callback) | - | - | User clicked an action button |
| onError(callback) | - | - | Error object |
| onDetailedError(callback) | - | - | Structured error with code + message |
| onReconnecting(callback) | - | - | Reconnection attempt state |
| onNetworkState(callback) | - | - | Network availability changed |
| onAppState(callback) | - | - | Tab visibility changed |
User & Topics
| Option | Type | Required | Description |
|---|---|---|---|
| setUserId(id) | - | - | Set the current user identifier |
| clearUserId() | - | - | Async. Clear the user identifier and detach the user on the server |
| subscribeTopic(topic) | - | - | Subscribe to a topic |
| unsubscribeTopic(topic) | - | - | Unsubscribe from a topic |
Badge + Analytics + Logs
| Option | Type | Required | Description |
|---|---|---|---|
| setBadgeCount(count) | - | - | Set the app badge count |
| clearBadge() | - | - | Clear the app badge |
| getBadgeCount() | - | - | Get the current badge count |
| setAnalyticsHandler(callback) | - | - | Forward SDK events to your analytics service |
| disableAnalytics() | - | - | Stop forwarding SDK events |
| setLogLevel(level) | - | - | Set the SDK log verbosity |
Browser Support#
- Chrome 50+ (Desktop & Android)
- Firefox 44+
- Edge 17+
- Safari 16+ (macOS only — iOS does not support Web Push)
- Opera 37+
Requirements#
- HTTPS required (
localhostworks for development) - Service worker file must be in the public root directory
- User must grant notification permission via the browser's permission prompt
Best Practices#
Create one AbrovaPush instance per page — re-creating it on every render leaks listeners
The first time, call register() from a UI affordance where the user understands why notifications matter. After permission is granted, call it on every page load
Call AbrovaPush.isSupported() before showing notification UI — some browsers / private modes don't support Web Push
Call setUserId() once authentication completes so notifications target the right account across devices
Every callback returns an unsubscribe function — call it when components unmount to prevent stale handlers
When the user clicks a notification while the tab is closed, getInitialMessage() returns the payload on page load — use it for deep linking