The Definitive Guide to the JavaScript Notifications API
The Notifications API is a web technology that enables web developers to send and manage notifications directly from web applications.
Introduction to the Notifications API
The Notifications API lets a web application display system-level messages outside the browser tab — the small pop-ups your operating system shows in the corner of the screen. Because they appear even when the user has switched to another tab or app, notifications are a direct way to surface time-sensitive information: a new chat message, a finished upload, a calendar reminder.
This page covers the full lifecycle: checking that the API is available, asking the user for permission, creating and customizing notifications, reacting to clicks, and showing notifications from a service worker so they keep working after the page is closed. It ends with the rules that keep notifications from becoming annoying.
A few facts to set expectations before the first line of code:
- Notifications require an explicit permission grant from the user. You cannot show one until permission is
granted. - They only work over a secure context (
https://, orhttp://localhostduring development). - The look and behavior of a notification is controlled by the operating system, not your CSS. You supply the content; the OS decides how to render it.
Checking for support
Always feature-detect before using the API, so the code degrades gracefully in environments that lack it (older browsers, some embedded webviews, and server-side rendering):
if ('Notification' in window) {
// The Notifications API is available
} else {
console.log('This browser does not support notifications.');
}Understanding permissions
Permission state lives in the static Notification.permission property and is one of three string values:
'granted'— the user allowed notifications; you can show them.'denied'— the user blocked them; calls to show a notification are silently ignored.'default'— the user has not decided yet, which is treated like'denied'until you ask.
Request permission with Notification.requestPermission(). It returns a promise that resolves to the resulting permission string:
Notification.requestPermission().then((permission) => {
console.log('Permission:', permission); // 'granted', 'denied', or 'default'
});Browsers only let you call requestPermission() in response to a user gesture, such as a button click. Asking for permission automatically on page load is widely blocked and hurts the user experience — wait until the user does something that explains why notifications are useful.
A robust pattern checks the current state first and only prompts when the decision is still 'default':
async function ensurePermission() {
if (Notification.permission === 'granted') {
return true;
}
if (Notification.permission === 'denied') {
return false; // can't re-prompt; the user must change it in browser settings
}
const permission = await Notification.requestPermission();
return permission === 'granted';
}The promise-based form pairs naturally with async/await and the broader Promise API.
Creating and displaying notifications
Once permission is granted, construct a notification with the Notification constructor. The first argument is the title (required); the second is an optional options object.
if (Notification.permission === 'granted') {
new Notification('Hello, world!', {
body: 'Here is the body of the notification.',
icon: '/icon-192.png'
});
}The notification appears the moment the object is created — you do not call a separate "show" method.
Common options
The options object accepts many fields. The most useful ones:
| Option | Type | What it does |
|---|---|---|
body | string | The main text shown under the title. |
icon | URL | An image shown alongside the notification. |
badge | URL | A small monochrome icon used on devices with limited space (mostly mobile). |
image | URL | A larger image displayed in the notification body. |
tag | string | An ID that groups notifications; a new one with the same tag replaces the old one. |
data | any | Arbitrary data you can read back in the click handler. |
silent | boolean | When true, suppresses sound and vibration. |
requireInteraction | boolean | Keeps the notification on screen until the user dismisses it (desktop). |
lang / dir | string | Language and text direction hints. |
new Notification('New message', {
body: 'You have 1 unread message from Alex.',
icon: '/icon-192.png',
tag: 'chat-alex', // replacing an earlier "Alex" notification
data: { conversationId: 42 },
requireInteraction: true
});Avoiding notification spam with tag
The tag option is the simplest way to stop stacking duplicates. If three messages arrive from the same person, reusing one tag means the user sees a single, updated notification instead of three:
function notifyUnread(count) {
new Notification('Inbox', {
body: `You have ${count} unread messages.`,
tag: 'inbox-count' // each call updates the same notification
});
}Notification events
A Notification instance fires events you can listen to. The most important is click, fired when the user activates the notification:
const notification = new Notification('Interactive Notification', {
body: 'Click me to do something',
icon: '/icon-192.png'
});
notification.onclick = (event) => {
event.preventDefault(); // stop the browser's default focus behavior
window.open('https://example.com', '_blank');
notification.close(); // remove the notification once handled
};Other available events are show (displayed), error (failed to display), and close (dismissed). You can attach handlers with addEventListener too — see event handling in the DOM for the general pattern.
Closing notifications programmatically
Call close() to dismiss a notification yourself — handy for clearing a "downloading…" notice once the download finishes:
const note = new Notification('Downloading…', { tag: 'download' });
// later, when the work is done:
setTimeout(() => note.close(), 4000);Silent notifications
Set silent: true to display a notification without sound or vibration — appropriate for low-priority, ambient updates:
new Notification('Silent Notification', {
body: 'This is a silent notification.',
silent: true
});Notifications from a service worker
The plain Notification constructor only works while a page is open. To deliver notifications when your site is in the background — or in response to a server push message — show them from a service worker using ServiceWorkerRegistration.showNotification():
// In the page: ask the service worker to show a notification
navigator.serviceWorker.ready.then((registration) => {
registration.showNotification('Background-capable notification', {
body: 'This can be shown even after the tab is closed.',
icon: '/icon-192.png',
actions: [
{ action: 'open', title: 'Open' },
{ action: 'dismiss', title: 'Dismiss' }
]
});
});Service-worker notifications also support action buttons (the actions array), which the simple constructor does not. Handle clicks inside the service worker itself:
// In the service worker (sw.js)
self.addEventListener('notificationclick', (event) => {
event.notification.close();
if (event.action === 'open') {
event.waitUntil(clients.openWindow('/inbox'));
}
});This service-worker path is what powers true web push: a server sends a message, the Push API wakes the service worker, and the worker calls showNotification().
Browser support and caveats
- Secure context only. Notifications need HTTPS (or
localhost). They will not work on plainhttp://pages. - Permission is sticky. Once a user picks
'denied', you cannot prompt again from JavaScript — they must change it in the browser's site settings. Don't keep re-asking. - iOS Safari historically did not support web notifications; support arrived only for sites added to the Home Screen as a PWA. Always feature-detect.
- Appearance is OS-controlled. Don't rely on a specific size, position, or styling — focus on clear, short copy.
Best practices
To keep notifications a feature users welcome rather than mute:
- Ask in context, after a gesture. Request permission when the user has just done something that makes notifications obviously useful (e.g., enabling alerts), never on first load.
- Respect a "no". If permission is
'denied', stop. Repeated prompts are not even technically possible, and nagging in the UI erodes trust. - Be timely and relevant. Send only notifications the user would genuinely want at that moment.
- Use them sparingly. Group with
tag, batch where you can, and reserve notifications for what's truly important — over-notifying trains users to ignore or block you. - Make clicks meaningful. A click should take the user straight to the relevant content, not just to your homepage.
Conclusion
The JavaScript Notifications API lets web apps reach users with timely, system-level messages — both while a page is open and, through a service worker, after it has closed. The workflow is consistent: feature-detect, request permission in response to a user gesture, then create notifications with the Notification constructor (or showNotification() in a worker) and respond to click events. Combine that with disciplined, sparing use, and notifications become a feature users keep enabled instead of one they rush to turn off.