Notification Options — Rich Push Notifications with Actions and Media
In this tutorial, you will learn about Notification Options. We cover key concepts, practical examples, and best practices to help you master this topic.
Rich notification options include action buttons, images, badges, vibration patterns, sounds, and grouping — making PWA notifications as capable as native notifications on mobile and desktop.
What You'll Learn
By the end of this tutorial, you will understand all available notification options, how to use actions for user interaction, how to group related notifications, and how to customize the notification appearance.
Why It Matters
Basic text-only notifications get ignored. Rich notifications with action buttons, images, and clear grouping increase engagement rates by 2-3x. Well-designed notifications let users take action (reply, approve, snooze) directly from the notification, reducing the friction of reopening your app.
Real-World Use
A project management PWA sends notifications for task assignments with action buttons: "Accept", "Delegate", and "View Details". Accepting directly from the notification updates the task status without opening the app. The notification also shows the task priority color and due date.
Available Notification Options
Notification Options Overview
┌──────────────────────────────────────────────────────────┐
│ Notification │
├──────────────────────────────────────────────────────────┤
│ Icon: [●] Title: "Task Assigned" │
│ Body: "You have been assigned 'Update docs'" │
│ Image: [screenshot of task] │
│ Badge: [3] │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Accept │ │ Delegate │ │ Dismiss │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ────────────────────────────────────────── │
│ Silent mode: on/off Alert type: persistent │
└──────────────────────────────────────────────────────────┘
Think of notification options like the features of a smartphone notification. A basic notification is like a text message — just words. Rich notifications are like a message with photos, action buttons, and quick replies — far more useful and actionable.
Comprehensive Notification Example
self.addEventListener('push', event => {
const data = event.data.json();
const options = {
// Core content
body: data.body,
icon: data.icon || '/icons/icon-192.png',
image: data.image,
badge: '/icons/badge.png',
// Behavior
tag: data.tag || 'default',
renotify: false,
silent: false,
requireInteraction: true,
data: {
url: data.url,
messageId: data.id,
timestamp: Date.now()
},
// Actions (buttons)
actions: [
{
action: 'accept',
title: 'Accept',
icon: '/icons/check.png'
},
{
action: 'view',
title: 'View Details',
icon: '/icons/view.png'
},
{
action: 'dismiss',
title: 'Dismiss'
}
],
// Visuals
vibrate: [200, 100, 200],
sound: '/sounds/notification.mp3',
timestamp: Date.now(),
// Android-specific
priority: 'high',
appBadgeCount: 5
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
Action Buttons and Handling
self.addEventListener('notificationclick', event => {
event.notification.close();
const action = event.action;
const data = event.notification.data;
console.log('Action:', action, 'Data:', data);
switch (action) {
case 'accept':
// Accept the task via API
event.waitUntil(
fetch(`/api/tasks/${data.messageId}/accept`, {
method: 'POST'
}).then(() => {
console.log('Task accepted');
})
);
break;
case 'view':
// Open the task page
event.waitUntil(
clients.openWindow(data.url)
);
break;
case 'dismiss':
// Just close, no further action
console.log('Notification dismissed');
break;
default:
// Click on the notification body (not an action)
event.waitUntil(
clients.openWindow(data.url || '/')
);
break;
}
});
Notification Grouping with Tags
Use the tag property to replace or group related notifications:
// Tag-based notification grouping
self.addEventListener('push', event => {
const data = event.data.json();
// Use same tag for related notifications
// New notification with same tag replaces the old one
const options = {
body: data.body,
tag: data.type === 'chat' ? `chat-${data.chatId}` : `default-${data.type}`,
renotify: true, // Vibrate even if replacing
data: { ... }
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
Badge and App Badge Count
// Set app badge (number on icon)
self.addEventListener('push', event => {
const data = event.data.json();
// Update badge count
if (navigator.setAppBadge) {
navigator.setAppBadge(data.unreadCount || 0);
}
// Notification with badge icon (small monochrome icon)
const options = {
body: data.body,
badge: '/icons/badge.png', // 96x96 monochrome PNG
icon: '/icons/icon-192.png', // Full color icon
appBadgeCount: data.unreadCount
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
// Clear badge when user opens the app
self.addEventListener('notificationclick', event => {
if (navigator.clearAppBadge) {
navigator.clearAppBadge();
}
// ...
});
Vibration Patterns
// Different vibration patterns for different notification types
const VIBRATION_PATTERNS = {
message: [200, 100, 200], // Two short buzzes
urgent: [500, 200, 500, 200, 500], // Three long buzzes
alert: [200], // Single short buzz
silent: [] // No vibration
};
self.addEventListener('push', event => {
const data = event.data.json();
const pattern = VIBRATION_PATTERNS[data.priority] || VIBRATION_PATTERNS.message;
const options = {
body: data.body,
vibrate: pattern,
silent: data.priority === 'silent'
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
Timestamp and Priority
self.addEventListener('push', event => {
const data = event.data.json();
const options = {
body: data.body,
timestamp: data.timestamp || Date.now(), // Controls display order
urgent: data.urgent || false,
priority: data.urgent ? 'high' : 'default',
// On Android, high priority notifications
// show as heads-up (pop over other apps)
...(data.urgent && { priority: 'high', vibrate: [500, 200, 500] })
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
Common Mistakes
- Using too many actions. More than 3 action buttons gets truncated on most devices. Limit actions to 2-3 critical options.
- Not setting a tag. Without a tag, every notification is separate. Unread counts become unmanageable. Always set a meaningful tag.
- Ignoring renotify behavior. When replacing a notification with the same tag, set renotify: true if you want the device to vibrate/alert again.
- Badge images that are not monochrome. The badge icon must be monochrome (single color). Color images are not supported for badges.
- Not handling the default click action. Users may click the notification body, not an action button. Always handle the default case (event.action === '') by opening the app.
Practice Questions
- What does the tag property do in notification options?
- How do action buttons work and how are they handled?
- What is the difference between icon, badge, and image in notifications?
- How do you set a vibration pattern for different notification types?
- What happens when you set renotify: true with an existing tag?
Challenge: Create a chat notification system with: grouped notifications per conversation (tag), reply action button, different vibration patterns for messages vs mentions, badge count updates, and a 5-second auto-dismiss for non-urgent notifications.
FAQ
Mini Project
Build a notification system for a task management PWA with: three action buttons (Accept, Delegate, Dismiss), priority-based vibration patterns, task count badge, grouped notifications per project (using tags), and a default click that opens the specific task page. Test on both desktop and mobile.
What's Next
You have mastered notification content. Now learn how to handle notification permissions — requesting permission gracefully, handling denial, and respecting user preferences.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro