Chapter 53: Web Notifications API & Native Push

Interactive Notification Action Buttons

**Part 11: HTML5 APIs Part 2** — Chapter 53: Notifications API

LEARNING OBJECTIVES
  • Implement interactive notification buttons using the actions array.
  • Handle action callbacks inside the notificationclick service worker event.
  • Add inline text input replies (supported on mobile Android / Chrome).
  • Provide accessible fallbacks for desktop environments that do not display action buttons.
🎬 INTERACTIVE VISUAL PIPELINE Core Architecture Simulation
🌐
1. Input
Directives & Tags
⚙️
2. Parse
Tokenizer & AST
🌳
3. Layout
Box Model & Flow
🎨
4. Render
GPU Paint & Composite
PHASE 1: INPUT & DIRECTIVES
Browser receives declarative markup stream, parsing tag tokens and initializing component state.

📖 The Mental Model & Story

Imagine receiving a text message on your smartwatch. Instead of pulling your phone out of your pocket, unlocking it, opening the messaging app, and navigating to the chat, you can simply tap a quick button ("Accept", "Archive", or "Reply") directly on the watch face.

Notification action buttons empower users to complete transactional workflows (accept meeting, snooze alarm, approve pull request) with zero context switching.

+-------------------------------------------------------------+
| 🔔 GitPlatform: Pull Request #402 Assigned to You          |
| "feat: migrate to native Popover API and Top Layer"         |
|                                                             |
| [  👍 Approve PR  ]   [  💬 Request Changes  ]   [ Dismiss ]|
+-------------------------------------------------------------+

Technical Deep Dive & Specifications

// Register notification with action buttons
self.registration.showNotification('Calendar Invite', {
  body: 'Architecture Review with Principal Engineers in 10 mins',
  icon: '/images/cal.png',
  actions: [
    { action: 'accept', title: '✅ Accept', icon: '/images/check.png' },
    { action: 'decline', title: '❌ Decline', icon: '/images/cross.png' }
  ],
  data: { meetingId: '109283' }
});

// Handling specific actions in sw.js
self.addEventListener('notificationclick', (event) => {
  event.notification.close();

  if (event.action === 'accept') {
    // Send API request in background without opening tab
    event.waitUntil(
      fetch(`/api/meetings/${event.notification.data.meetingId}/respond`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ status: 'ACCEPTED' })
      })
    );
  } else if (event.action === 'decline') {
    event.waitUntil(
      fetch(`/api/meetings/${event.notification.data.meetingId}/respond`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ status: 'DECLINED' })
      })
    );
  } else {
    // User clicked the notification body itself -> Open dashboard
    event.waitUntil(clients.openWindow('/calendar'));
  }
});

📌 Key Takeaways

  • Maximum 2–3 action buttons are supported across OS notifications.
  • Actions can trigger background REST API calls using fetch() without bringing up a browser window.
  • Always provide a fallback click behavior for when users click the notification body itself.
  • --

❓ Knowledge Check

1. Which of the following is correct?

2. Which of the following is correct?

🏋️ Study Exercise

Task: Review the javascript example above. Identify the key directives and their purpose, then try writing your own version from memory.

// Register notification with action buttons self.registration.showNotification('Calendar Invite', { body: 'Architecture Review with Principal Engineers in 10 mins', icon: '/images/cal.png', actions: [ { action: 'accept', title: '✅ Accept', icon: '/images/check.png' }, { action: 'decline', title: '❌ Decline', icon: '/images/cross.png' } ], data: { meetingId: '109283' } }); // Handling specific actions in sw.js self.addEventListener('notificationclick', (event) => { event.notification.close(); if (event.action === 'accept') { // Send API request in background without opening tab event.waitUntil( fetch(`/api/meetings/${event.notification.data.meetingId}/respond`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status: 'ACCEPTED' }) }) ); } else if (event.action === 'decline') { event.waitUntil( fetch(`/api/meetings/${event.notification.data.meetingId}/respond`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status: 'DECLINED' }) }) ); } else { // User clicked the notification body itself -> Open dashboard event.waitUntil(clients.openWindow('/calendar')); } });