Web Push Implementation Guide
Overview
This guide provides complete documentation for implementing Web Push notifications using the Metrix Web SDK. Web Push enables you to send browser notifications to users who have granted permission.
Key Concepts
User Association
- Anonymous Users: If
authorizeUseris not called before permission is granted, an anonymous user is created in Metrix for each device that grants push notification permission. - Identified Users: Call
authorizeUserbefore requesting permission to associate the subscription with an identified user.
Subscription Lifecycle
- Initialize SDK with push configuration including VAPID public key
- Configure Service Worker (either Metrix-provided or existing)
- Request Permission using one of four available methods
- Receive Notifications through Metrix dashboard
Prerequisites
- HTTPS: Required for production (localhost allowed for development)
- Modern Browser: Chrome 50+, Firefox 44+, Edge 15+, Safari 16+
- Service Worker Support: Browser must support Service Workers and Push API
- VAPID Public Key: Provided by Metrix
You can use the following sample implementations to add Web Push to your website:
Implementation Steps
Step 1: Service Worker Setup
Option A: Use Metrix Service Worker (Recommended)
- Download
metrix-sw.js - Place at your website root (same directory as index.html)
- File must be accessible at:
https://YOUR_DOMAIN/metrix-sw.js
When hasSW: false (default), the SDK automatically registers this worker.
Option B: Use Existing Service Worker
If you already have a service worker:
// Add to your service worker file
importScripts('https://cdn.metrix.ir/sdk/web/metrixweb-sw.js');Then initialize with hasSW: true:
init('APP_ID', 'API_KEY', {
push: {
enabled: true,
publicKey: 'YOUR_VAPID_PUBLIC_KEY',
hasSW: true,
},
});Step 2: SDK Initialization
import { init } from '@metrixorg/websdk';
init('APP_ID', 'API_KEY', {
push: {
enabled: true,
publicKey: 'YOUR_VAPID_PUBLIC_KEY',
hasSW: false, // Set to true if using existing service worker
showBell: true, // Optional: Display bell icon
showBackdrop: false, // Optional: Show permission dialog on init
popUpText: 'Enable notifications', // Custom bell popup text
position: 'bottom-right', // Bell position
},
});Step 3: Request User Permission
Method 1: subscribePush() - Programmatic Request
import { subscribePush } from '@metrixorg/websdk';
// Call from a user action (button click, etc.)
async function enableNotifications() {
try {
const state = await subscribePush();
if (state === 'subscribed') {
console.log('✓ Push notifications enabled');
} else if (state === 'blocked') {
console.log('✗ User blocked notifications');
} else if (state === 'closed') {
console.log('○ Permission dialog closed');
}
} catch (error) {
console.error('Failed to enable push:', error);
}
}
// Attach to button or user action
document.getElementById('enable-btn').addEventListener('click', enableNotifications);Method 2: Automatic Backdrop on Init
init('APP_ID', 'API_KEY', {
push: {
enabled: true,
publicKey: 'YOUR_VAPID_PUBLIC_KEY',
showBackdrop: true,
backdropDelay: 2000, // 2 second delay
backdropText: 'Enable notifications to stay updated',
},
});Method 3: Manual Backdrop Control
import { openPushConfirmBackdrop } from '@metrixorg/websdk';
async function showPermissionDialog() {
const state = await openPushConfirmBackdrop({
backdropText: 'Get important updates',
backdropDelay: 500,
});
console.log('User decision:', state);
}
// Show dialog after user interaction
document.getElementById('settings-btn').addEventListener('click', showPermissionDialog);Method 4: Bell Icon
init('APP_ID', 'API_KEY', {
push: {
enabled: true,
publicKey: 'YOUR_VAPID_PUBLIC_KEY',
showBell: true,
popUpText: 'Click to enable notifications',
position: 'bottom-right', // or top-left, top-right, bottom-left
},
});Users click the bell to initiate the permission flow.
Response States
All permission methods return a ConfirmPushState:
type ConfirmPushState = 'subscribed' | 'blocked' | 'closed';| State | Meaning |
|---|---|
subscribed | User granted permission, subscription created |
blocked | Browser permission denied or user blocked |
closed | Dialog closed without user action |
API Reference
Exported Functions
subscribePush()
Request notification permission from user interaction.
subscribePush(): Promise<ConfirmPushState>Returns: Promise that resolves with user’s decision
Usage:
const state = await subscribePush();openPushConfirmBackdrop(config)
Display permission dialog manually.
openPushConfirmBackdrop(config: {
backdropText: string;
backdropDelay?: number;
}): Promise<ConfirmPushState>Parameters:
backdropText(required): Dialog message textbackdropDelay(optional): Delay in milliseconds before showing
Usage:
const state = await openPushConfirmBackdrop({
backdropText: 'Enable notifications',
backdropDelay: 1000,
});onPushStateResolved()
Wait for permission flow completion from any method.
onPushStateResolved(): Promise<ConfirmPushState>Returns: Promise resolving with final permission state
Usage:
const finalState = await onPushStateResolved();notificationPermissionState()
Check current browser permission state.
notificationPermissionState(): NotificationPermission | undefinedReturns: 'default' | 'granted' | 'denied' | undefined
Usage:
const permission = notificationPermissionState();
if (permission === 'granted') {
console.log('Already enabled');
}getPushSubscription()
Get current push subscription details.
getPushSubscription(): Promise<PushSubscriptionJSON | undefined>Returns: Push subscription object or undefined
Usage:
const subscription = await getPushSubscription();
if (subscription) {
console.log('Endpoint:', subscription.endpoint);
}Configuration Options
interface PushConfig {
enabled: boolean; // Enable push (required)
publicKey: string; // VAPID public key (required)
hasSW?: boolean; // Existing service worker? (default: false)
showBell?: boolean; // Show bell icon? (default: false)
showBackdrop?: boolean; // Show backdrop on init? (default: false)
backdropText?: string; // Backdrop message text
backdropDelay?: number; // Backdrop delay in ms
popUpText?: string; // Bell popup text
position?: string; // Bell position (top-left, top-right, bottom-left, bottom-right)
}Complete Example
import {
init,
authorizeUser,
subscribePush,
notificationPermissionState,
getPushSubscription,
} from '@metrixorg/websdk';
// Initialize SDK
init('APP_ID', 'API_KEY', {
push: {
enabled: true,
publicKey: 'YOUR_VAPID_PUBLIC_KEY',
hasSW: false,
},
});
// Authorize user (optional but recommended)
authorizeUser('user@example.com');
// Add permission request button
document.getElementById('notify-btn').addEventListener('click', async () => {
const permission = notificationPermissionState();
// Skip if already granted or denied
if (permission === 'granted') {
alert('Notifications already enabled');
return;
}
if (permission === 'denied') {
alert('Enable notifications in browser settings');
return;
}
// Request permission
try {
const state = await subscribePush();
if (state === 'subscribed') {
const sub = await getPushSubscription();
console.log('Subscribed:', sub?.endpoint);
alert('✓ Notifications enabled');
} else if (state === 'blocked') {
alert('Notifications blocked by browser');
} else {
alert('Permission request cancelled');
}
} catch (error) {
console.error('Push error:', error);
alert('Failed to enable notifications');
}
});Advanced Patterns
Delayed Permission Request
Request permission after user engagement:
import { init, openPushConfirmBackdrop } from '@metrixorg/websdk';
init('APP_ID', 'API_KEY', {
push: {
enabled: true,
publicKey: 'YOUR_VAPID_PUBLIC_KEY',
showBackdrop: false, // Don't show immediately
},
});
// Show after 5 seconds of user interaction
let hasInteracted = false;
document.addEventListener('click', () => {
if (!hasInteracted) {
hasInteracted = true;
setTimeout(() => {
openPushConfirmBackdrop({
backdropText: 'Stay updated with notifications',
backdropDelay: 500,
});
}, 5000);
}
});Conditional Permission Request
Request based on user action:
document.getElementById('checkout-btn').addEventListener('click', async () => {
// Complete checkout...
const permission = notificationPermissionState();
if (permission !== 'denied' && permission !== 'granted') {
const state = await subscribePush();
if (state === 'subscribed') {
console.log('User subscribed to order updates');
}
}
});Combined Bell + Manual Permission
Allow multiple ways to request:
init('APP_ID', 'API_KEY', {
push: {
enabled: true,
publicKey: 'YOUR_VAPID_PUBLIC_KEY',
showBell: true,
popUpText: 'Enable notifications',
position: 'bottom-right',
},
});
// Also add custom button
document.getElementById('custom-notify-btn').addEventListener('click', subscribePush);Troubleshooting
Service Worker Issues
Problem: Service worker not found
- Solution: Ensure
metrix-sw.jsis at domain root, accessible viahttps://YOUR_DOMAIN/metrix-sw.js
Problem: “Service worker is not for Metrix”
- Solution: Set
hasSW: trueand verifyimportScripts()is in your service worker
Permission Issues
Problem: Permission always denied
- Solution: Check
publicKeyis correct VAPID key (not private key)
Problem: Permission prompt not appearing
- Solution: Call
subscribePush()from user interaction (click, tap, etc.)
Problem: Push not initializing
- Solution: Verify
enabled: trueandpublicKeyare provided in config
Browser Support
Problem: “Push notification is not supported”
- Solution: Use modern browser (Chrome 50+, Firefox 44+, Edge 15+, Safari 16+)
Problem: HTTPS required error
- Solution: Use HTTPS in production, localhost allowed for development
Best Practices
- Timing: Request permission after user engagement, not on page load
- Context: Explain benefits before requesting permission
- User Identification: Call
authorizeUser()before requesting permission - Error Handling: Always handle rejected promises
- Fallback: Don’t require push; make it optional
- Testing: Use HTTPS or localhost with proper VAPID keys
- Privacy: Respect user choice; don’t re-prompt after denial
- Monitoring: Log permission states for analytics
Debugging
Enable logging to debug issues:
// Check push status
const permission = notificationPermissionState();
console.log('Push initialized:', permission !== undefined);
console.log('Permission:', permission);
// Check subscription
getPushSubscription().then(sub => {
console.log('Subscription:', sub?.endpoint ? 'Active' : 'None');
});
// Monitor flow
onPushStateResolved().then(state => {
console.log('Final state:', state);
});Support
For issues or questions:
- Review browser console for errors
- Check that HTTPS/localhost is used
- Verify VAPID public key is correct
- Ensure service worker file is accessible
- Confirm browser supports Push API