Badging for Home Screen Web Apps

Along with the many other features for web apps on iOS and iPadOS 16.4, WebKit now includes support for the W3C’s Badging API.

A badged web application icon in the iOS dock showing the number 15.

This straightforward API allows web developers to badge the icon of a web app. This article explores the various features of the Badging API and how to use it, and our ongoing collaboration with W3C’s Web Applications Working Group to improve the specification.

Home Screen Web App Availability

In iOS and iPadOS 16.4, the Badging API is available exclusively for web apps the user has added to their home screen. You won’t find the API exposed to websites in Safari or other browsers, or in any app that uses WKWebView.

You can easily check for the availability of the API using feature detection for the setAppBadge function:

if ('setAppBadge' in navigator) {
  // API is available...
}  

Badges and Notifications

On iOS and iPadOS, badging has always been closely tied to notifications.

Apps built for macOS, iOS, and iPadOS are free to set their badge counts through platform specific APIs whenever they like. The user must grant the app permission to display notifications before the badge will appear.

Similarly, web apps are free to call setAppBadge() whenever they like, but the badge will only appear if the user has granted notifications permission.

To request notification permission, call Notification.requestPermission() as part of a user activation (such as a button click):

<button onclick="requestNotificationPermission()">
  Request notifications permission
</button>

<script>
  async function requestNotificationPermission() {
    const permission = await Notification.requestPermission();
    if (permission === 'granted') {
      // You can now use the Badging API
    }
  }
</script>

iOS and iPadOS will show this system prompt to the user for permission:

A browser permission prompt for asking the user to allow web notifications.

You can also check if a user has previously granted notifications permission by using the Permissions API:

async function checkNotificationPermission() {
  const permissionStatus = await navigator
    .permissions
    .query({ name: 'notifications' });

  switch (permissionStatus.state) {
    case 'granted':
      // You can use the Badging API
      break;
    case 'denied':
      // The user has denied the permission
      break;
    default:
      // The user has not yet granted or denied the permission
      await requestNotificationPermission();
      break;
  }
}
checkNotificationPermission();

Note that even though the user may have granted notifications permission, they remain in control of that permission through iOS or iPadOS Settings. As some users find badges distracting, they might leave the notifications permission enabled, but choose not to show the badge.

To retain user privacy, we never expose this user preference to the web app.

Setting and Clearing a Badge From a Web App

App badges represent the number of items requiring the user’s attention (e.g., “you have 5 unread messages“). What the number means is application dependent.

An application badge icon showing a badge with the number 5.

To update the application icon badge, pass a positive number to the navigator.setAppBadge() method:

async function setBadge(count) {
  if ('setAppBadge' in navigator) {
    try {
      await navigator.setAppBadge(count);
    } catch (error) {
      console.error('Failed to set app badge:', error);
    }
  }
}

// Set the badge count to 5
setBadge(5);

You can clear the badge by using navigator.clearAppBadge().

async function clearBadge() {
  if ('clearAppBadge' in navigator) {
    try {
      await navigator.clearAppBadge();
    } catch (error) {
      console.error('Failed to clear app badge:', error);
    }
  }
}

// Clear the badge
clearBadge();

Alternatively, calling navigator.setAppBadge(0) is equivalent to calling navigator.clearAppBadge().

Using the API From a Service Worker

In addition to being exposed to window objects, the Badging API is exposed in Web Worker contexts.

This makes it particularly useful for apps that support Web Push as it is trivial to update your application badge while your Service Worker handles a push event.

// Function to determine the badge count based on the event data
function determineBadgeCount(data) {
  // Process the data to compute the badge count
}

self.addEventListener('push', (event) => {
  let promises = [];

  if ('setAppBadge' in self.navigator) {
    const badgeCount = determineBadgeCount(event.data);
    // Promise to set the badge
    const promise = self.navigator.setAppBadge(badgeCount);
    promises.push(promise);
  }

  // Promise to show a notification
  promises.push(self.registration.showNotification("You've got mail!"));

  // Finally...
  event.waitUntil(Promise.all(promises));
});

When requesting a push subscription you must promise that all pushes will be user visible events. This means that each push event you process must result in a user visible notification being displayed with a call to self.registration.showNotification()

You are encouraged to update your application badge when handling a push event, but a badge update by itself does not fulfill the “user visible” requirement; Keep showing those notifications!

Security Restrictions for Third-Party

When the user added your web app to their Home Screen, they were indicating their trust in you as a first party.

JavaScript that calls navigator.setAppBadge() in an attempt to update the badge count must come from a frame that is the same-origin as your top-level document. Calls to navigator.setAppBadge() from cross-origin frames have no effect.

Evolving the Badging API at the W3C

We’re collaborating with the W3C Web Applications Working Group and its members, particularly with Microsoft who have partnered with us as co-editors of the Badging API specification. This collaboration allows us to create a more consistent, secure, and privacy-preserving API across browsers and operating systems. The Badging API specification was previously edited by folks from Google. We’re excited to work with the W3C membership to continue its evolution.