The Notifications API
The Notifications API lets your web application display desktop notifications to users even when they’re not looking at your tab. Whether it’s a new message, a task reminder, or a background process completing, notifications keep users informed in real-time.
In this tutorial, you’ll learn how to request permission, create notifications, handle user interactions, and build a complete notifications system for your web app.
Checking for Notification Support
Before using the Notifications API, check if the browser supports it:
if ('Notification' in window) {
console.log('Notifications API supported!');
} else {
console.log('Notifications not supported in this browser');
}
Most modern browsers support the Notifications API, but it’s good practice to check anyway.
Requesting Permission
Before you can display any notification, you must explicitly ask the user for permission. This is a deliberate design choice — browsers require user consent to prevent abuse.
async function requestNotificationPermission() {
if (!('Notification' in window)) {
console.log('This browser does not support notifications');
return;
}
if (Notification.permission === 'granted') {
console.log('Notification permission already granted');
return;
}
if (Notification.permission !== 'denied') {
const permission = await Notification.requestPermission();
console.log('Permission status:', permission);
}
}
// Call it when appropriate (e.g., on a button click)
requestNotificationPermission();
The Notification.requestPermission() method returns a promise that resolves to one of three values:
granted— The user has given permissiondenied— The user has blocked notificationsdefault— The user dismissed the permission prompt
Notice that you can only request permission once. If the user denies it, you cannot ask again — you’d need to guide them to manually enable notifications in browser settings.
Creating Notifications
Once you have permission, creating a notification is straightforward:
function showNotification(title, options = {}) {
if (Notification.permission !== 'granted') {
console.log('No permission to show notifications');
return;
}
const notification = new Notification(title, {
body: options.body || '',
icon: options.icon || '/notification-icon.png',
badge: options.badge || '/badge-icon.png',
tag: options.tag || '',
requireInteraction: options.requireInteraction || false,
data: options.data || {}
});
// Handle notification click
notification.onclick = () => {
window.focus();
notification.close();
};
// Handle notification close
notification.onclose = () => {
console.log('Notification closed');
};
return notification;
}
// Usage
showNotification('New Message', {
body: 'You have a new message from Alice',
icon: '/icons/message.png',
tag: 'message-123'
});
The Notification constructor accepts a title and an options object with several useful properties:
body— The notification text below the titleicon— URL of an image to show as the notification iconbadge— A small icon shown in the taskbartag— A string identifier for grouping notificationsrequireInteraction— Keeps the notification visible until the user interacts with itdata— Arbitrary data you want to attach to the notification
Handling Notification Events
Notifications can trigger several events you can listen to:
const notification = new Notification('Task Complete', {
body: 'Your download has finished'
});
// When user clicks the notification
notification.onclick = (event) => {
console.log('Notification clicked');
window.focus();
// Navigate to the relevant page
window.location.href = '/downloads';
};
// When notification is closed (by user or auto)
notification.onclose = (event) => {
console.log('Notification closed after', event.target.timeout, 'ms');
};
// When an error occurs
notification.onerror = (event) => {
console.error('Notification error:', event);
};
You can also use the onshow event to track when the notification is actually displayed:
notification.onshow = (event) => {
console.log('Notification shown at', new Date());
// Auto-close after 5 seconds
setTimeout(() => {
notification.close();
}, 5000);
};
Notification Channels (Advanced)
On some browsers (notably Chrome and Edge), you can create notification channels to give users fine-grained control over different types of notifications:
// Note: This works primarily in Chrome/Edge
function createNotificationChannel() {
if (!('NotificationChannel' in window)) {
console.log('Notification channels not supported');
return;
}
const channel = new NotificationChannel('messages', {
description: 'Notifications about new messages',
importance: NotificationManager.GENERIC,
lightColor: '#007bff',
sound: '/sounds/message.mp3'
});
channel.addEventListener('notificationclick', (event) => {
console.log('Message notification clicked');
event.notification.close();
});
return channel;
}
// In practice, you'd register this during service worker setup
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then((registration) => {
// Create notification channel via service worker
console.log('Service Worker ready for notifications');
});
}
Note: Notification channels require a service worker to work properly — something we’ll cover in a future tutorial about push notifications.
Best Practices
Here are some tips for using notifications effectively:
-
Ask at the right time — Don’t ask for permission immediately when the page loads. Wait until the user has engaged with your app and understands why notifications would be useful.
-
Respect the user’s choice — If permission is denied, don’t keep asking. Instead, show a subtle UI element explaining how they can enable notifications in browser settings.
-
Don’t over-notify — Too many notifications annoy users and lead them to block your site entirely.
-
Include useful data — Use the
dataoption to attach context to notifications so you can handle clicks intelligently. -
Handle permission states — Always check
Notification.permissionbefore attempting to create notifications.
A Complete Example
Here’s a practical example combining everything we’ve learned:
class NotificationManager {
constructor() {
this.permission = Notification.permission;
}
async init() {
if (this.permission === 'default') {
await this.requestPermission();
}
return this.permission === 'granted';
}
async requestPermission() {
this.permission = await Notification.requestPermission();
return this.permission;
}
notify(title, options = {}) {
if (this.permission !== 'granted') {
console.warn('Cannot notify: permission not granted');
return null;
}
const notification = new Notification(title, {
icon: '/icons/app-icon.png',
badge: '/icons/badge.png',
...options
});
return notification;
}
notifyNewMessage(sender, preview) {
return this.notify('New message from ' + sender, {
body: preview,
tag: 'messages',
requireInteraction: true,
data: { type: 'message', sender }
});
}
}
// Usage
const notifications = new NotificationManager();
document.getElementById('enable-btn').addEventListener('click', async () => {
const granted = await notifications.init();
if (granted) {
console.log('Notifications enabled!');
notifications.notifyNewMessage('Alice', 'Hey, are you free tonight?');
}
});
Summary
The Notifications API is a powerful way to re-engage users even when they’ve navigated away from your site. Remember these key points:
- Always request permission before creating notifications
- Handle the three permission states: granted, denied, and default
- Use event listeners to respond to user interactions
- Follow best practices to avoid annoying your users
In the next tutorial, we’ll explore the Clipboard API, another useful browser API for interacting with the user’s system.