CodingNic

Browser APIs

Notifications API

Browser APIs 15 min read

Notifications API

Objectives

By the end of this chapter, you should be able to:

  • Check and request notification permission
  • Show a system notification with new Notification()
  • Respond to a user clicking a notification

๐Ÿ’ก Why this matters: A notification shows up outside the browser tab entirely, in the operating system’s own notification area, even if the user has switched to a different app. It’s the browser’s way of reaching a user who isn’t actively looking at the page.

โš ๏ธ A note on verification: the same limitation as the last two lessons applies. Notifications require a real OS-level notification system and a real permission decision, neither exists in this sandbox. The code below is accurate, based on the standardized Notifications API, run it yourself in a browser to see it firsthand.

What You’re Building

A task reminder: type a task, set how many seconds until it’s due, and get an actual system notification, complete with an icon, when the time’s up, even if you’ve switched to another tab by then.

text
task-reminder/
โ”œโ”€โ”€ index.html
โ”œโ”€โ”€ script.js
โ””โ”€โ”€ icon.png
html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Task Reminder</title>
</head>
<body>
  <h1>Task Reminder</h1>
  <input id="taskInput" type="text" placeholder="Task name">
  <input id="secondsInput" type="number" placeholder="Remind me in (seconds)">
  <button id="enableBtn">Enable Reminders</button>
  <button id="scheduleBtn">Schedule Reminder</button>
  <p id="reminderStatus"></p>

  <script src="script.js"></script>
</body>
</html>

icon.png is a small square image sitting alongside index.html, it never appears directly in the page, the notification itself displays it. Everything else from here on goes in script.js.

Step 1: Check Current Permission

Notification.permission is always one of three strings: "default" (never asked), "granted", or "denied". Check it before doing anything else, no point offering a feature that’s already blocked.

javascript
const enableBtn = document.getElementById("enableBtn");
const reminderStatus = document.getElementById("reminderStatus");

if (Notification.permission === "granted") {
  reminderStatus.textContent = "Reminders are enabled";
  enableBtn.style.display = "none";
} else if (Notification.permission === "denied") {
  reminderStatus.textContent = "Reminders are blocked in your browser settings";
  enableBtn.style.display = "none";
} else {
  reminderStatus.textContent = "Enable reminders to get notified";
}

On a first visit, permission is "default", so the “Enable Reminders” button stays visible and the other two branches don’t apply yet.

Step 2: Request Permission on Click

Notification.requestPermission() shows the browser’s permission prompt, only in response to a real user action, like this button click, never automatically on page load. It’s promise-based.

javascript
enableBtn.addEventListener("click", async () => {
  const permission = await Notification.requestPermission();

  if (permission === "granted") {
    reminderStatus.textContent = "Reminders enabled!";
    enableBtn.style.display = "none";
  } else {
    reminderStatus.textContent = "Reminders weren't enabled";
  }
});

Step 3: Schedule a Reminder

This is where Module 3’s setTimeout() comes back. “Remind me in 10 seconds” is just a timer that shows a notification when it fires, instead of logging something.

javascript
const taskInput = document.getElementById("taskInput");
const secondsInput = document.getElementById("secondsInput");
const scheduleBtn = document.getElementById("scheduleBtn");

scheduleBtn.addEventListener("click", () => {
  const taskName = taskInput.value.trim();
  const seconds = Number(secondsInput.value);

  if (!taskName || !seconds) return;

  reminderStatus.textContent = `Reminder set for "${taskName}" in ${seconds}s`;

  setTimeout(() => {
    if (Notification.permission === "granted") {
      new Notification("Task due", {
        body: taskName,
        icon: "icon.png",
      });
    }
  }, seconds * 1000);
});

icon.png, the file sitting next to index.html, shows up inside the notification itself, in the OS notification tray, next to the title and body text. Notice the Notification.permission === "granted" check happens again here, right before actually showing the notification, not just back in Step 1. Permission could theoretically change between scheduling the reminder and it firing, checking again at the last moment is the safe habit.

Step 4: Respond to a Click

A notification supports .onclick, the same idea as any other event handler. This is where window.focus() earns its keep, bringing the tab back into view if the user was somewhere else when the reminder fired.

javascript
setTimeout(() => {
  if (Notification.permission === "granted") {
    const notification = new Notification("Task due", {
      body: taskName,
      icon: "icon.png",
    });

    notification.onclick = () => {
      window.focus();
      reminderStatus.textContent = `Back to review: ${taskName}`;
      notification.close();
    };
  }
}, seconds * 1000);

Clicking the notification (which might be sitting in the OS notification tray, not the page itself) brings the tab into focus, updates the status text, and closes the notification, .close() dismisses it immediately rather than waiting for it to disappear on its own.

The Complete script.js

javascript
const enableBtn = document.getElementById("enableBtn");
const reminderStatus = document.getElementById("reminderStatus");
const taskInput = document.getElementById("taskInput");
const secondsInput = document.getElementById("secondsInput");
const scheduleBtn = document.getElementById("scheduleBtn");

if (Notification.permission === "granted") {
  reminderStatus.textContent = "Reminders are enabled";
  enableBtn.style.display = "none";
} else if (Notification.permission === "denied") {
  reminderStatus.textContent = "Reminders are blocked in your browser settings";
  enableBtn.style.display = "none";
} else {
  reminderStatus.textContent = "Enable reminders to get notified";
}

enableBtn.addEventListener("click", async () => {
  const permission = await Notification.requestPermission();
  if (permission === "granted") {
    reminderStatus.textContent = "Reminders enabled!";
    enableBtn.style.display = "none";
  } else {
    reminderStatus.textContent = "Reminders weren't enabled";
  }
});

scheduleBtn.addEventListener("click", () => {
  const taskName = taskInput.value.trim();
  const seconds = Number(secondsInput.value);

  if (!taskName || !seconds) return;

  reminderStatus.textContent = `Reminder set for "${taskName}" in ${seconds}s`;

  setTimeout(() => {
    if (Notification.permission === "granted") {
      const notification = new Notification("Task due", {
        body: taskName,
        icon: "icon.png",
      });

      notification.onclick = () => {
        window.focus();
        reminderStatus.textContent = `Back to review: ${taskName}`;
        notification.close();
      };
    }
  }, seconds * 1000);
});

Try It

Build the full task reminder page from this lesson:

  1. Wire up Step 1 and Step 2, confirm #reminderStatus correctly reflects all three permission states.
  2. Wire up Step 3, scheduling a reminder with setTimeout() and showing a notification (with icon.png) once permission is granted.
  3. Add the .onclick handler from Step 4.
  4. Add a guard so scheduleBtn’s click handler does nothing (and shows a message asking the user to enable reminders first) if Notification.permission isn’t "granted".

Recap

  • Notification.permission is "default", "granted", or "denied". Notification.requestPermission() asks the user and returns a promise resolving to the same values, this lesson’s “Enable Reminders” button used it directly.
  • new Notification(title, options) shows a notification once permission is granted, options.body adds detail text and options.icon sets the image shown alongside it.
  • .onclick on a notification handles the user clicking it, often paired with window.focus() and .close().
  • Ask for permission in response to a real action, and re-check Notification.permission right before showing a notification that was scheduled earlier, not just once upfront.

Next lesson: web workers, running JavaScript on a separate thread so heavy work doesn’t freeze the page.