# Notification actions and badge options

> How Notification actions buttons and the badge image option work, their persistent-notification requirement, and current browser support per MDN.

**In one line:** `actions` adds up to `Notification.maxActions` clickable buttons to a
notification, and `badge` is a separate option — a small image URL the OS shows "when there is
not enough space to display the notification itself," per MDN, such as the Android
notification bar. Both are options on service-worker-driven notifications, not the app-icon
Badging API.

## What each option does

- **`actions`** — a read-only array MDN describes as objects with `action` (an identifying
  string), `title` (button label text), an optional `icon` URL, and an optional `navigate` URL.
  Per MDN, "browsers typically limit the maximum number of actions they will display," and that
  limit is exposed as the static `Notification.maxActions` property.
- **`badge`** — per MDN, "a string containing the URL of an image" shown in place of the full
  notification when space is tight; MDN's Android guidance sizes it "about 96 by 96 px," noting
  the image "will be automatically masked."

```js
const permission = await Notification.requestPermission();
if (permission === 'granted') {
  const registration = await navigator.serviceWorker.ready;
  await registration.showNotification('New message', {
    body: 'Ada sent you a message.',
    badge: '/icons/badge-96.png',
    actions: [
      { action: 'reply', title: 'Reply' },
      { action: 'dismiss', title: 'Dismiss' },
    ],
  });
}
```

## The persistent-notification requirement

Per MDN, `actions` is available "only for persistent notifications" — those shown via
`ServiceWorkerRegistration.showNotification()` — not for a plain `new Notification()`. MDN
states passing a non-`null` `actions` option to the `Notification()` constructor "throws a
`TypeError`."

What happens when an action button is clicked depends on whether that action set a `navigate`
URL. Per MDN's `actions` documentation, if `navigate` is set the browser navigates to that URL
directly and does not dispatch `notificationclick`. Only when an action has no `navigate` URL
does the click reach the service worker's `notificationclick` event, where `event.action` holds
the clicked button's `action` string.

## Where it is supported

Per the linked browser-compat-data:

| Browser | `actions` | `badge` |
|---|---|---|
| Chrome (Desktop) | yes, since 53 | yes, since 53 |
| Edge (Desktop) | yes, since 18 | yes, since 18 |
| Opera (Desktop) | yes, since 39 | yes, since 39 |
| Firefox (Desktop) | yes, since 152 | not supported |
| Safari | not supported | not supported |

Both options require a secure context, per MDN.

## How to detect it at runtime

```js
function canShowActionsAndBadge() {
  if (!('serviceWorker' in navigator) || !('Notification' in window)) {
    return { actions: false, badge: false }; // no persistent notifications here at all
  }
  return {
    actions: 'actions' in Notification.prototype,
    badge: 'badge' in Notification.prototype,
  };
}

async function notifyNewMessage(registration) {
  const { actions, badge } = canShowActionsAndBadge();
  const options = { body: 'Ada sent you a message.' };
  if (badge) {
    options.badge = '/icons/badge-96.png';
  }
  if (actions) {
    options.actions = [
      { action: 'reply', title: 'Reply' },
      { action: 'dismiss', title: 'Dismiss' },
    ];
  }
  // When actions/badge are unsupported, this sends a body-only notification
  // instead — the options above are simply omitted rather than causing an error.
  await registration.showNotification('New message', options);
}
```

## Practical checklist

- [ ] Never pass an `actions` option to `new Notification()` — per MDN it throws a `TypeError`
      outside a persistent (service-worker) notification.
- [ ] Read `Notification.maxActions` instead of hardcoding a button count; browsers cap it
      differently.
- [ ] Do not confuse the `badge` option's small tray image with the separate app-icon Badging
      API (`navigator.setAppBadge()`) — they solve different problems.
- [ ] For actions with a `navigate` URL, expect a direct browser navigation instead of a
      `notificationclick` event; only actions without `navigate` reach the service worker's
      `notificationclick` event, where you read `event.action`.
- [ ] Check the support table above before relying on `actions` in Firefox or on either option
      in Safari.

## Where to go next

- [The Notifications API](/reference/notifications/notifications-api/) — the base permission
  model and page-vs-service-worker distinction these options build on.
- [App icon badging](/reference/installation/badging/) — the separate Badging API for setting
  a count on the installed app's icon.