Displaying a Notification

Alexey Rodionov
Alexey Rodionov
Matt Gaunt

The notification options is split into two sections, one that deals with the visual aspects (this section) and one that explains the behavioral aspects of notifications (the next section).

You can play around with various notification options in various browsers on various platforms using Peter Beverloo's Notification Generator.

The API for showing a notification is simple:

<ServiceWorkerRegistration>.showNotification(<title>, <options>);

Both arguments, title and options are optional.

The title is a string and options can be any of the following:

{
  "//": "Visual Options",
  "body": "<String>",
  "icon": "<URL String>",
  "image": "<URL String>",
  "badge": "<URL String>",
  "dir": "<String of 'auto' | 'ltr' | 'rtl'>",
  "timestamp": "<Long>"

  "//": "Both visual & behavioral options",
  "actions": "<Array of Strings>",
  "data": "<Anything>",

  "//": "Behavioral Options",
  "tag": "<String>",
  "requireInteraction": "<boolean>",
  "renotify": "<Boolean>",
  "vibrate": "<Array of Integers>",
  "sound": "<URL String>",
  "silent": "<Boolean>",
}

Let's look at the visual options:

Dissection of the UI of a Notification.

Title and body options

This is what a notification looks like without the title and options in Chrome on Windows:

Notification without the title and options in Chrome on Windows.

As you can see, the browser name is used as the title and the "New notification" placeholder is used as the notification body.

If a progressive web application is installed on the device, the web app name will be used instead of the browser name:

Notification with the web app name instead of the browser name.

If we ran the following code:

const title = 'Simple Title';

const options = {
  body: 'Simple piece of body text.\nSecond line of body text :)',
};

registration.showNotification(title, options);

we'd get this notification in Chrome on Linux:

Notification with title and body text in Chrome on Linux.

In Firefox on Linux it would look like this:

Notification with title and body text in Firefox on Linux.

This is what the notification with a lot of text in the title and body looks like in Chrome on Linux:

Notification with long title and body text in Chrome on Linux.

Firefox on Linux collapses the body text until you hover the notification, causing the notification to expand:

Notification with long title and body text in Firefox on Linux.

Notification with long title and body text in Firefox on Linux while hovering over the notification with the mouse cursor.

The same notifications in Firefox on Windows look like this:

Notification with title and body text in Firefox on Windows.

Notification with long title and body text in Firefox on Windows.

As you can see, the same notification may look different in different browsers. It may also look different in the same browser on different platforms.

Chrome and Firefox use the system notifications and notification center on platforms where these are available.

For example, system notifications on macOS don't support images and actions (buttons and inline replies).

Chrome also has a custom notifications for all desktop platforms. You can enable it by setting the chrome://flags/#enable-system-notifications flag to the Disabled state.

Icon

The icon option is essentially a small image you can show next to the title and body text.

In your code you need to provide a URL to the image you'd like to load:

const title = 'Icon Notification';

const options = {
  icon: '/images/demos/icon-512x512.png',
};

registration.showNotification(title, options);

You get this notification in Chrome on Linux:

Notification with icon in Chrome on Linux.

and in Firefox on Linux:

Notification with icon in Firefox on Linux.

Sadly there aren't any solid guidelines for what size image to use for an icon.

Android seems to want a 64dp image (which is 64px multiples by the device pixel ratio).

Assuming the highest pixel ratio for a device is 3, an icon size of 192px or more is a safe bet.

Badge

The badge is a small monochrome icon that is used to portray a little more information to the user about where the notification is from:

const title = 'Badge Notification';

const options = {
  badge: '/images/demos/badge-128x128.png',
};

registration.showNotification(title, options);

At the time of writing the badge is only used in Chrome on Android.

Notification with badge in Chrome on Android.

On other browsers (or Chrome without the badge), you'll see an icon of the browser.

Notification with badge in Firefox on Android.

As with the icon option, there are no real guidelines on what size to use.

Digging through Android guidelines the recommended size is 24px multiplied by the device pixel ratio.

Meaning an image of 72px or more should be good (assuming a max device pixel ratio of 3).

Image

The image option can be used to display a larger image to the user. This is particularly useful to display a preview image to the user.

const title = 'Image Notification';

const options = {
  image: '/images/demos/unsplash-farzad-nazifi-1600x1100.jpg',
};

registration.showNotification(title, options);

In Chrome on Linux the notification will look like this:

Notification with image in Chrome on Linux.

In Chrome on Android the cropping and ratio are different:

Notification with image in Chrome on Android.

Given the differences in ratio between desktop and mobile, it's extremely hard to suggest guidelines.

Since Chrome on desktop doesn't fill the available space and has a ratio of 4:3, perhaps the best approach is to serve an image with this ratio and allow Android to crop the image. That being said, the image option may still change.

On Android, the only guideline is a width of 450dp.

Using this guideline, an image of width 1350px or more would be a good bet.

Actions (Buttons)

You can define actions to display buttons with a notification:

const title = 'Actions Notification';

const options = {
  actions: [
    {
      action: 'coffee-action',
      type: 'button',
      title: 'Coffee',
      icon: '/images/demos/action-1-128x128.png',
    },
    {
      action: 'doughnut-action',
      type: 'button',
      title: 'Doughnut',
      icon: '/images/demos/action-2-128x128.png',
    },
    {
      action: 'gramophone-action',
      type: 'button',
      title: 'Gramophone',
      icon: '/images/demos/action-3-128x128.png',
    },
    {
      action: 'atom-action',
      type: 'button',
      title: 'Atom',
      icon: '/images/demos/action-4-128x128.png',
    },
  ],
};

registration.showNotification(title, options);

For each action you can define a title, an action (which is essentially an ID), an icon, and a type. The title and icon is what you can see in the notification. The ID is used when detecting that the action button had been clicked (more about this in the next section). The type can be omitted because 'button' is the default value.

At the time of writing only Chrome and Opera for Android support actions.

In the example above there are four actions defined to illustrate that you can define more actions than will be displayed. If you want to know the number of actions that will be displayed by the browser, you can check window.Notification?.maxActions:

const maxVisibleActions = window.Notification?.maxActions;

if (maxVisibleActions) {
  options.body = `Up to ${maxVisibleActions} notification actions can be displayed.`;
} else {
  options.body = 'Notification actions are not supported.';
}

On desktop, the action button icons display their colors (see the pink doughnut):

Notification with action buttons on Chrome on Linux.

On Android 6 and earlier, the icons are colored to match the system color scheme:

Notification with action buttons on Chrome for Android.

On Android 7 and later, the action icons aren't shown at all.

Chrome will hopefully change it's behavior on desktop to match Android (i.e. apply the appropriate color scheme to make the icons match the system look and feel). In the meantime, you can match Chrome's text color by making your icons have a color of #333333.

It's also worth calling out that icons look crisp on Android but not on desktop.

The best size I could get to work on desktop Chrome was 24px x 24px. This sadly looks out of place on Android.

The best practice we can draw from these differences:

  • Stick to a consistent color scheme for your icons so at least all your icons are consistently displayed to the user.
  • Make sure they work in monochrome as some platforms may display them that way.
  • Test the size and see what works for you. 128px × 128px works well on Android for me but was poor quality on desktop.
  • Expect your action icons not to be displayed at all.

The Notification spec is exploring a way to define multiple sizes of icons, but it looks like it'll be some time before anything is agreed upon.

Actions (Inline Replies)

You can add an inline reply to the notification by defining an action with the 'text' type:

const title = 'Alexey Rodionov';

const options = {
  body: 'How are you doing? )',
  image: '/images/demos/avatar-512x512.jpg',
  icon: '/images/demos/icon-512x512.png',
  badge: '/images/demos/badge-128x128.png',
  actions: [
    {
      action: 'reply',
      type: 'text',
      title: 'Reply',
      icon: '/images/demos/action-5-128x128.png',
    }
  ],
};

registration.showNotification(title, options);

This is what it will look like on Android:

Notification on Android with a reply action button.

Clicking on the action button opens a text input field:

Notification on Android with an opened text input field.

You can customize the placeholder for the text input field:

const title = 'Alexey Rodionov';

const options = {
  body: 'How are you doing? )',
  icon: '/images/demos/avatar-512x512.jpg',
  badge: '/images/demos/badge-128x128.png',
  actions: [
    {
      action: 'reply',
      type: 'text',
      title: 'Reply',
      icon: '/images/demos/action-5-128x128.png',
      placeholder: 'Type text here',
    }
  ],
};

registration.showNotification(title, options);

Notification on Android with customized placeholder for text input field.

In Chrome on Windows, the text input field is always visible without having to click the action button:

Notification on Windows with a text input field and a reply action button.

You can add more than one inline reply or combine buttons and inline replies:

const title = 'Poll';

const options = {
  body: 'Do you like this photo?',
  image: '/images/demos/cat-image.jpg',
  icon: '/images/demos/icon-512x512.png',
  badge: '/images/demos/badge-128x128.png',
  actions: [
    {
      action: 'yes',
      type: 'button',
      title: '👍 Yes',
    },
    {
      action: 'no',
      type: 'text',
      title: '👎 No (explain why)',
      placeholder: 'Type your explanation here',
    },
  ],
};

registration.showNotification(title, options);

Notification on Windows with a text input field and two action buttons.

Direction

The dir parameter allows you to define which direction the text should be displayed, right-to-left or left-to-right.

In testing, it seemed that the direction was largely determined by the text rather than this parameter. According to the spec, this is intended to suggest to the browser how to layout options like actions, but I saw no difference.

Probably best to define if you can, otherwise the browser should do the right thing according to the text supplied.

The parameter should be set to either auto, ltr or rtl.

A right-to-left language used on Chrome on Linux looks like this:

Notification with right-to-left language on Chrome on Linux.

In Firefox (while hovering over it) you'll get this:

Notification with right-to-left language on Firefox on Linux.

Vibrate

The vibrate option allows you to define a vibration pattern that'll run when a notification is displayed, assuming the user's current settings allow for vibrations (i.e. the device isn't in silent mode).

The format of the vibrate option should be an array of numbers that describe the number of milliseconds the device should vibrate, followed by the number of milliseconds the device should not vibrate.

const title = 'Vibrate Notification';

const options = {
  // Star Wars shamelessly taken from the awesome Peter Beverloo
  // https://tests.peter.sh/notification-generator/
  vibrate: [
    500, 110, 500, 110, 450, 110, 200, 110, 170, 40, 450, 110, 200, 110, 170,
    40, 500,
  ],
};

registration.showNotification(title, options);

This only affects devices that support vibration.

Sound

The sound parameter allows you to define a sound to play when the notification is received.

At the time of writing, no browser has support for this option.

const title = 'Sound Notification';

const options = {
  sound: '/demos/notification-examples/audio/notification-sound.mp3',
};

registration.showNotification(title, options);

Timestamp

Timestamp allows you to tell the platform the time when an event occurred that resulted in the push notification being sent.

The timestamp should be the number of milliseconds since 00:00:00 UTC, which is 1 January 1970 (which is the UNIX epoch).

const title = 'Timestamp Notification';

const options = {
  body: 'Timestamp is set to "01 Jan 2000 00:00:00".',
  timestamp: Date.parse('01 Jan 2000 00:00:00'),
};

registration.showNotification(title, options);

UX Best Practices

The biggest UX failure I've seen with notifications is a lack of specificity in the information displayed by a notification.

You should consider why you sent the push message in the first place and make sure all of the notification options are used to help users understand why they are reading that notification.

To be honest, it's easy to see examples and think "I'll never make that mistake". But it's easier to fall into that trap than you might think.

Some common pitfalls to avoid:

  • Don't put your website in the title or the body. Browsers include your domain in the notification so don't duplicate it.
  • Use all the information that you have available. If you send a push message because someone sent a message to a user, rather than using a title of 'New Message' and body of 'Click here to read it.' use a title of 'John just sent a new message' and set the body of the notification to part of the message.

Browsers and feature detection

At the time of writing, there is a pretty big disparity between Chrome and Firefox in terms of feature support for notifications.

Luckily, you can detect support for notification features by looking at the window.Notification prototype.

Let's say we wanted to know if a notification supports action buttons, we'd do the following:

if ('actions' in window.Notification?.prototype) {
  // Action buttons are supported.
} else {
  // Action buttons are NOT supported.
}

With this, we could change the notification we display to our users.

With the other options, just do the same as above, replacing 'actions' with the desired parameter name.

Where to go next

Code labs