Public Holidays Coupon Code Code Compiler

Web Push Notification on a Browser Using JavaScript


Oct 5, 2025

Web Push Notification on a Browser Using JavaScript

Web push notifications have revolutionized the way websites communicate with their users, enabling real-time engagement even when users are not actively browsing your site. In this comprehensive guide, you'll learn how to implement web push notifications in your browser using JavaScript, service workers, and the Push Notification API.

Whether you're building an e-commerce platform, a news portal, or a social media application, understanding how to leverage browser notifications can significantly improve user engagement and retention. This tutorial will walk you through the complete implementation process with clear, step-by-step instructions.

What Are Web Push Notifications?

Web push notifications are clickable messages that appear on a user's device, sent by websites through the browser. Unlike traditional notifications that require the user to be actively browsing your site, push notifications can be delivered even when the browser is closed, making them an incredibly powerful tool for re-engaging users.

These notifications work through three main components: the Notification API, the Push API, and Service Workers. Together, they enable websites to request permission from users, send notifications from the server, and display them on the user's device across different platforms including desktop and mobile browsers.

Prerequisites

Before we dive into the implementation, ensure you have the following:

  • body: The main content of the notification message
  • icon: A large image displayed in the notification (recommended size: 192x192px)
  • badge: A small monochrome icon for the notification tray (recommended size: 96x96px)
  • image: A large image displayed within the notification body
  • vibrate: A vibration pattern for mobile devices (array of milliseconds)
  • tag: A unique identifier to group or replace notifications
  • requireInteraction: Keep notification visible until user interacts (use sparingly)
  • actions: Array of action buttons with customizable labels and handlers
  • silent: Suppress notification sound and vibration
  • timestamp: When the notification was created or should be displayed

Best Practices for User Experience

When implementing web push notifications, follow these guidelines to ensure a positive user experience:

  • Request Permission at the Right Time: Don't ask for notification permission immediately when users land on your site. Wait until they've had a chance to explore your content and understand the value of notifications
  • Provide Context: Explain why notifications are beneficial before requesting permission. Users are more likely to grant permission if they understand the value
  • Be Relevant and Timely: Only send notifications that provide genuine value. Irrelevant or excessive notifications will lead users to revoke permissions
  • Allow Easy Opt-Out: Provide clear settings where users can manage their notification preferences or disable them entirely
  • Test Thoroughly: Test notifications on different devices, browsers, and operating systems to ensure consistent behavior
  • Handle Permission States: Gracefully handle all three permission states (granted, denied, and default) in your application logic

Implementing Server-Side Push Notifications

While this tutorial focuses on client-side notifications, production applications typically send notifications from a server. Here's a basic example of how to implement server-side push using Node.js:

Step 5: Install Web Push Library (Server-Side)

5 If you want to send notifications from a server, you'll need to install the web-push library. First, initialize your Node.js project and install the dependency:

npm init -y
npm install web-push express body-parser

Step 6: Generate VAPID Keys

6 VAPID (Voluntary Application Server Identification) keys are required for server push. Create a file named generate-keys.js:

const webpush = require('web-push');

// Generate VAPID keys
const vapidKeys = webpush.generateVAPIDKeys();

console.log('Public Key:', vapidKeys.publicKey);
console.log('Private Key:', vapidKeys.privateKey);

// Save these keys securely - you'll need them for your server

Run this file once to generate your keys:

node generate-keys.js
Important: Store your private key securely and never commit it to version control. The public key can be shared with your client-side application.

Step 7: Create Server-Side Push Endpoint

7 Create a file named server.js to handle push notification requests:

const express = require('express');
const webpush = require('web-push');
const bodyParser = require('body-parser');
const path = require('path');

const app = express();
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname)));

// VAPID keys (replace with your generated keys)
const vapidKeys = {
    publicKey: 'YOUR_PUBLIC_KEY_HERE',
    privateKey: 'YOUR_PRIVATE_KEY_HERE'
};

// Configure web-push with VAPID keys
webpush.setVapidDetails(
    'mailto:your-email@example.com',
    vapidKeys.publicKey,
    vapidKeys.privateKey
);

// Store subscriptions (in production, use a database)
let subscriptions = [];

// Endpoint to receive subscription from client
app.post('/subscribe', (req, res) => {
    const subscription = req.body;
    subscriptions.push(subscription);
    
    console.log('New subscription received:', subscription);
    res.status(201).json({ message: 'Subscription saved successfully' });
});

// Endpoint to send push notification
app.post('/send-notification', (req, res) => {
    const notificationPayload = {
        title: req.body.title || 'Server Push Notification',
        body: req.body.body || 'This notification was sent from the server!',
        icon: req.body.icon || 'https://via.placeholder.com/192x192.png?text=Server',
        url: req.body.url || '/'
    };
    
    // Send notification to all subscribed clients
    const promises = subscriptions.map(subscription => {
        return webpush.sendNotification(
            subscription,
            JSON.stringify(notificationPayload)
        ).catch(error => {
            console.error('Error sending notification:', error);
            // Remove invalid subscriptions
            if (error.statusCode === 410) {
                subscriptions = subscriptions.filter(sub => sub !== subscription);
            }
        });
    });
    
    Promise.all(promises)
        .then(() => {
            res.status(200).json({ 
                message: 'Notifications sent successfully',
                count: subscriptions.length 
            });
        })
        .catch(error => {
            console.error('Error in sending notifications:', error);
            res.status(500).json({ error: 'Failed to send notifications' });
        });
});

// Endpoint to get public VAPID key
app.get('/vapid-public-key', (req, res) => {
    res.status(200).json({ publicKey: vapidKeys.publicKey });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
    console.log(`Public VAPID Key: ${vapidKeys.publicKey}`);
});

Step 8: Update Client-Side Code for Server Push

8 Modify your app.js to subscribe to server push notifications. Add this function:

// Function to subscribe to push notifications from server
async function subscribeToServerPush() {
    try {
        const registration = await navigator.serviceWorker.ready;
        
        // Get public VAPID key from server
        const response = await fetch('/vapid-public-key');
        const { publicKey } = await response.json();
        
        // Subscribe to push notifications
        const subscription = await registration.pushManager.subscribe({
            userVisibleOnly: true,
            applicationServerKey: urlBase64ToUint8Array(publicKey)
        });
        
        // Send subscription to server
        await fetch('/subscribe', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(subscription)
        });
        
        console.log('Successfully subscribed to server push:', subscription);
    } catch (error) {
        console.error('Failed to subscribe to server push:', error);
    }
}

// Helper function to convert VAPID key
function urlBase64ToUint8Array(base64String) {
    const padding = '='.repeat((4 - base64String.length % 4) % 4);
    const base64 = (base64String + padding)
        .replace(/\-/g, '+')
        .replace(/_/g, '/');
    
    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);
    
    for (let i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i);
    }
    return outputArray;
}

// Call this after permission is granted
if (Notification.permission === 'granted') {
    subscribeToServerPush();
}

Common Issues and Troubleshooting

Service Worker Not Registering

Problem: The service worker fails to register with errors in the console.

Solutions:

  • Ensure you're using HTTPS or localhost (service workers require secure contexts)
  • Check that the service worker file path is correct and accessible
  • Verify there are no JavaScript syntax errors in your service-worker.js file
  • Clear browser cache and unregister existing service workers in DevTools

Notifications Not Appearing

Problem: Permission is granted but notifications don't show up.

Solutions:

  • Check browser notification settings - ensure notifications aren't blocked at the OS level
  • Verify the service worker is active in DevTools → Application → Service Workers
  • Check browser console for errors during notification creation
  • Ensure Do Not Disturb mode is disabled on your device
  • Try using registration.showNotification() instead of new Notification()

Permission Already Denied

Problem: User previously denied permission and can't be prompted again.

Solutions:

  • Provide clear instructions on how to manually enable notifications in browser settings
  • Display a message explaining the benefits of enabling notifications
  • Add a link to browser-specific help documentation

Security Considerations

When implementing web push notifications, keep these security practices in mind:

  • HTTPS Only: Always serve your application over HTTPS in production to ensure secure communication
  • VAPID Keys: Keep your private VAPID key confidential and store it securely using environment variables
  • Input Validation: Validate and sanitize all notification content to prevent XSS attacks
  • Rate Limiting: Implement rate limiting on your server to prevent notification spam
  • Subscription Management: Store subscriptions securely and implement proper authentication
  • Content Security Policy: Configure appropriate CSP headers to protect against malicious scripts

Performance Optimization

To ensure optimal performance with web push notifications:

  • Lazy Load Service Worker: Register the service worker after the page has loaded to avoid blocking initial render
  • Optimize Notification Images: Use compressed, appropriately-sized images for icons and badges
  • Batch Notifications: Group related notifications instead of sending multiple individual ones
  • Cache Service Worker: Implement proper caching strategies in your service worker for offline functionality
  • Monitor Subscriptions: Regularly clean up expired or invalid subscriptions from your database

Real-World Use Cases

Web push notifications are valuable in various scenarios:

  • E-commerce: Order confirmations, shipping updates, price drop alerts, and cart abandonment reminders
  • News and Media: Breaking news alerts, personalized content recommendations, and live event updates
  • Social Media: Friend requests, messages, comments, and activity updates
  • Productivity Apps: Task reminders, meeting notifications, and deadline alerts
  • Financial Services: Transaction alerts, account activity, and security notifications
  • Healthcare: Appointment reminders, medication schedules, and health tips

Analytics and Tracking

To measure the effectiveness of your notification strategy, implement tracking for:

  • Permission grant/deny rates
  • Notification delivery success rates
  • Click-through rates (CTR)
  • Conversion rates from notifications
  • Opt-out rates and reasons
  • Optimal timing for sending notifications

Future of Web Push Notifications

The web push notification ecosystem continues to evolve with new features and improvements:

  • Rich Media Support: Enhanced support for images, videos, and interactive elements within notifications
  • Advanced Targeting: More sophisticated segmentation and personalization capabilities
  • Cross-Platform Consistency: Improved standardization across different browsers and operating systems
  • AI-Powered Optimization: Smart delivery timing based on user behavior and engagement patterns
  • Privacy Enhancements: Stronger user controls and privacy protection mechanisms

Conclusion

Implementing web push notifications in your browser using JavaScript is a powerful way to engage users and keep them informed about important updates. By following this comprehensive guide, you've learned how to:

  • Set up the complete project structure with HTML, CSS, and JavaScript
  • Register and configure service workers for background notification handling
  • Request user permission and manage notification states
  • Create and display rich, interactive notifications
  • Implement server-side push notifications using Node.js and the web-push library
  • Handle common issues and follow security best practices

Remember that the key to successful notification implementation is respecting user preferences and providing genuine value. Always ask for permission at the right moment, send relevant content, and make it easy for users to manage their notification settings.

With web push notifications, you can create engaging user experiences that keep your audience connected to your web application, even when they're not actively browsing. Start implementing this powerful feature today and watch your user engagement metrics improve!

Next Steps:
  • Test your implementation across different browsers and devices
  • Implement analytics to track notification performance
  • Create a notification preferences page for users
  • Integrate with your backend system for automated notifications
  • A/B test different notification strategies to optimize engagement
  • Basic understanding of HTML, CSS, and JavaScript
  • A modern web browser (Chrome, Firefox, Safari, or Edge)
  • A local development server (you can use VS Code with Live Server extension)
  • HTTPS protocol (required for service workers - use localhost for testing)
Note: Service workers and the Push API only work over HTTPS (except for localhost during development). This is a security requirement to prevent man-in-the-middle attacks.

Project Structure

Let's set up our project structure. Create a new folder for your project and organize the files as shown below:

Create the following file structure:

web-push-notification/
│
├── index.html          (Main HTML file)
├── style.css           (Styling for the page)
├── app.js              (Main application logic)
└── service-worker.js   (Service worker file)

Step 1: Create the HTML Structure

1 First, create the index.html file in your project root directory. This file will contain the user interface for requesting notification permissions and triggering notifications.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Push Notifications Demo</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>Web Push Notifications</h1>
        <p>Enable notifications to receive updates from this website</p>
        
        <div class="notification-status">
            <p>Notification Status: <span id="status">Checking...</span></p>
        </div>
        
        <div class="button-group">
            <button id="enable-notifications" class="btn btn-primary">
                Enable Notifications
            </button>
            <button id="send-notification" class="btn btn-secondary" disabled>
                Send Test Notification
            </button>
        </div>
        
        <div class="info-box">
            <h3>How it works:</h3>
            <ol>
                <li>Click "Enable Notifications" to request permission</li>
                <li>Accept the browser permission prompt</li>
                <li>Click "Send Test Notification" to see it in action</li>
            </ol>
        </div>
    </div>
    
    <script src="app.js"></script>
</body>
</html>

Step 2: Add CSS Styling

2 Create the style.css file in your project root directory to style the notification interface.

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 20px;
}

.container {
    background: white;
    padding: 40px;
    border-radius: 16px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
    max-width: 600px;
    width: 100%;
}

h1 {
    color: #01AEEF;
    margin-bottom: 10px;
    font-size: 2.5em;
}

p {
    color: #666;
    margin-bottom: 20px;
    line-height: 1.6;
}

.notification-status {
    background-color: #f8f9fa;
    padding: 15px;
    border-radius: 8px;
    margin: 20px 0;
    border-left: 4px solid #01AEEF;
}

#status {
    font-weight: bold;
    color: #01AEEF;
}

.button-group {
    display: flex;
    gap: 15px;
    margin: 25px 0;
    flex-wrap: wrap;
}

.btn {
    padding: 12px 24px;
    border: none;
    border-radius: 8px;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.3s ease;
    flex: 1;
    min-width: 200px;
}

.btn-primary {
    background-color: #01AEEF;
    color: white;
}

.btn-primary:hover {
    background-color: #0D7FA8;
    transform: translateY(-2px);
    box-shadow: 0 5px 15px rgba(1, 174, 239, 0.3);
}

.btn-secondary {
    background-color: #28a745;
    color: white;
}

.btn-secondary:hover:not(:disabled) {
    background-color: #218838;
    transform: translateY(-2px);
    box-shadow: 0 5px 15px rgba(40, 167, 69, 0.3);
}

.btn:disabled {
    background-color: #cccccc;
    cursor: not-allowed;
    opacity: 0.6;
}

.info-box {
    background-color: #e7f6fd;
    padding: 20px;
    border-radius: 8px;
    margin-top: 25px;
}

.info-box h3 {
    color: #0D7FA8;
    margin-bottom: 15px;
}

.info-box ol {
    padding-left: 20px;
}

.info-box li {
    margin-bottom: 8px;
    color: #555;
}

Step 3: Implement the Main Application Logic

3 Create the app.js file in your project root directory. This file will handle service worker registration, permission requests, and notification triggering.

// Check if browser supports notifications and service workers
if ('Notification' in window && 'serviceWorker' in navigator) {
    console.log('Browser supports notifications and service workers');
} else {
    console.log('Browser does not support notifications or service workers');
    document.getElementById('status').textContent = 'Not Supported';
}

// Get DOM elements
const enableBtn = document.getElementById('enable-notifications');
const sendBtn = document.getElementById('send-notification');
const statusText = document.getElementById('status');

// Update status based on current permission
function updateNotificationStatus() {
    const permission = Notification.permission;
    
    if (permission === 'granted') {
        statusText.textContent = 'Enabled ✓';
        statusText.style.color = '#28a745';
        enableBtn.textContent = 'Notifications Enabled';
        enableBtn.disabled = true;
        sendBtn.disabled = false;
    } else if (permission === 'denied') {
        statusText.textContent = 'Blocked ✗';
        statusText.style.color = '#dc3545';
        enableBtn.disabled = true;
    } else {
        statusText.textContent = 'Disabled';
        statusText.style.color = '#ffc107';
    }
}

// Register service worker
async function registerServiceWorker() {
    try {
        const registration = await navigator.serviceWorker.register('/service-worker.js');
        console.log('Service Worker registered successfully:', registration);
        return registration;
    } catch (error) {
        console.error('Service Worker registration failed:', error);
        throw error;
    }
}

// Request notification permission
async function requestNotificationPermission() {
    try {
        // First, register the service worker
        await registerServiceWorker();
        
        // Request notification permission
        const permission = await Notification.requestPermission();
        
        if (permission === 'granted') {
            console.log('Notification permission granted');
            updateNotificationStatus();
        } else if (permission === 'denied') {
            alert('Notification permission denied. Please enable it in your browser settings.');
        } else {
            alert('Notification permission dismissed.');
        }
    } catch (error) {
        console.error('Error requesting notification permission:', error);
        alert('Failed to enable notifications. Please try again.');
    }
}

// Send a test notification
function sendTestNotification() {
    if (Notification.permission !== 'granted') {
        alert('Please enable notifications first!');
        return;
    }
    
    // Get service worker registration
    navigator.serviceWorker.ready.then(function(registration) {
        // Notification options
        const options = {
            body: 'This is a test notification from your web application!',
            icon: 'https://via.placeholder.com/192x192.png?text=Web+Push',
            badge: 'https://via.placeholder.com/96x96.png?text=Badge',
            vibrate: [200, 100, 200],
            tag: 'test-notification',
            requireInteraction: false,
            actions: [
                {
                    action: 'open',
                    title: 'Open App'
                },
                {
                    action: 'close',
                    title: 'Close'
                }
            ],
            data: {
                url: window.location.origin,
                timestamp: Date.now()
            }
        };
        
        // Show notification through service worker
        registration.showNotification('Web Push Notification', options);
        console.log('Test notification sent!');
    });
}

// Event listeners
enableBtn.addEventListener('click', requestNotificationPermission);
sendBtn.addEventListener('click', sendTestNotification);

// Check notification status on page load
updateNotificationStatus();

// Register service worker on page load
if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('/service-worker.js')
            .then(registration => {
                console.log('Service Worker registered on load:', registration.scope);
            })
            .catch(error => {
                console.error('Service Worker registration failed on load:', error);
            });
    });
}

Step 4: Create the Service Worker

4 Create the service-worker.js file in your project root directory. The service worker handles background processes and notification events.

// Service Worker version
const CACHE_VERSION = 'v1';
const CACHE_NAME = 'web-push-cache-' + CACHE_VERSION;

// Install event - caching assets
self.addEventListener('install', (event) => {
    console.log('Service Worker: Installing...');
    
    event.waitUntil(
        caches.open(CACHE_NAME).then((cache) => {
            console.log('Service Worker: Caching files');
            return cache.addAll([
                '/',
                '/index.html',
                '/style.css',
                '/app.js'
            ]);
        })
    );
    
    // Force the service worker to become active immediately
    self.skipWaiting();
});

// Activate event - cleanup old caches
self.addEventListener('activate', (event) => {
    console.log('Service Worker: Activating...');
    
    event.waitUntil(
        caches.keys().then((cacheNames) => {
            return Promise.all(
                cacheNames.map((cacheName) => {
                    if (cacheName !== CACHE_NAME) {
                        console.log('Service Worker: Deleting old cache:', cacheName);
                        return caches.delete(cacheName);
                    }
                })
            );
        })
    );
    
    // Take control of all pages immediately
    return self.clients.claim();
});

// Fetch event - serve from cache when offline
self.addEventListener('fetch', (event) => {
    event.respondWith(
        caches.match(event.request).then((response) => {
            return response || fetch(event.request);
        })
    );
});

// Push event - handle incoming push notifications
self.addEventListener('push', (event) => {
    console.log('Service Worker: Push notification received');
    
    let notificationData = {
        title: 'New Notification',
        body: 'You have a new message!',
        icon: 'https://via.placeholder.com/192x192.png?text=Push',
        badge: 'https://via.placeholder.com/96x96.png?text=Badge'
    };
    
    // Parse push data if available
    if (event.data) {
        try {
            notificationData = event.data.json();
        } catch (e) {
            notificationData.body = event.data.text();
        }
    }
    
    const options = {
        body: notificationData.body,
        icon: notificationData.icon || 'https://via.placeholder.com/192x192.png?text=Push',
        badge: notificationData.badge || 'https://via.placeholder.com/96x96.png?text=Badge',
        vibrate: [200, 100, 200],
        tag: 'push-notification',
        requireInteraction: false,
        data: {
            url: notificationData.url || '/',
            timestamp: Date.now()
        }
    };
    
    event.waitUntil(
        self.registration.showNotification(notificationData.title, options)
    );
});

// Notification click event
self.addEventListener('notificationclick', (event) => {
    console.log('Service Worker: Notification clicked');
    
    event.notification.close();
    
    // Handle notification click actions
    if (event.action === 'open') {
        event.waitUntil(
            clients.openWindow(event.notification.data.url || '/')
        );
    } else if (event.action === 'close') {
        // Just close the notification
        console.log('Notification closed by user');
    } else {
        // Default action - open the app
        event.waitUntil(
            clients.matchAll({ type: 'window', includeUncontrolled: true })
                .then((clientList) => {
                    // Check if there's already a window open
                    for (let client of clientList) {
                        if (client.url === event.notification.data.url && 'focus' in client) {
                            return client.focus();
                        }
                    }
                    // If no window is open, open a new one
                    if (clients.openWindow) {
                        return clients.openWindow(event.notification.data.url || '/');
                    }
                })
        );
    }
});

// Notification close event
self.addEventListener('notificationclose', (event) => {
    console.log('Service Worker: Notification was closed', event.notification.tag);
});

How It Works: Understanding the Flow

Registration and Permission Flow

When a user first visits your website, the application checks if the browser supports web push notifications. Upon clicking the "Enable Notifications" button, the following sequence occurs:

  1. Service Worker Registration: The browser registers the service worker file, which runs in the background independently of your web page
  2. Permission Request: The browser displays a native permission prompt asking the user to allow or block notifications
  3. Status Update: Based on the user's response, the application updates the UI to reflect the current permission state
  4. Notification Ready: Once permission is granted, the "Send Test Notification" button becomes active

Notification Lifecycle

When you send a notification, it goes through several stages:

  1. Creation: The notification is created with a title, body, icon, and optional action buttons
  2. Display: The browser displays the notification according to the operating system's notification style
  3. Interaction: Users can click on the notification or its action buttons, triggering the corresponding event handlers in the service worker
  4. Closure: The notification automatically closes after a timeout or when the user dismisses it

Testing Your Implementation

To test your web push notification implementation:

Testing Steps:
  1. Start a local web server in your project directory (e.g., using VS Code Live Server or Python's http.server)
  2. Open your browser and navigate to http://localhost:PORT
  3. Click "Enable Notifications" and accept the permission prompt
  4. Click "Send Test Notification" to see the notification appear
  5. Try clicking on the notification to see the click handler in action

Browser Compatibility

The Push Notification API is widely supported across modern browsers:

  • Chrome: Full support since version 42
  • Firefox: Full support since version 44
  • Edge: Full support since version 17
  • Safari: Support on macOS and iOS 16.4+
  • Opera: Full support since version 29

Copyright 2025. All rights are reserved