Skip to content

Declarative Web Push support #2300

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

Soxasora
Copy link
Member

@Soxasora Soxasora commented Jul 20, 2025

Description

Closes #2298
Adds support for Declarative Web Push notifications, enabling them right now on browsers that support it (e.g. Safari >= 18.4) and automatically on other browsers in the near future.

Highlight of this PR is the capability to navigate to URLs natively by the browser on Apple devices.

Screenshots

Demonstration of correct URL navigation by a Declarative Web Push notification

Screen.Recording.2025-07-20.at.12.00.13.mp4

Demo Badge count manipulation and URL navigation

cropped_declarative_potential.mp4

Additional Context

To handle badging declaratively we need to find new ways to count notifications, sending JSON payloads with the app_badge property.
For the moment only devices with Legacy Push API will have badging support.

What is Declarative Web Push, and why we want to support it This new specification packs features like service worker-less notifications and better privacy, but in our case the most interesting part is that it's Apple native (for now), potentially enabling things that were broken before on Apple devices:
  • URL navigation on notification click
  • simplified app badge count
  • push subscription loss protection

Ultimately, less code, only a declarative JSON payload.

These benefits of course will be extended to all browsers, automatically, once they catch up.

In the meantime, they're backwards compatible:

If your push message arrives to a newer browser, it’s handled declaratively by the browser. If it arrives to an older browser, it’s handled imperatively by JavaScript as it always had been.1


Some other context

When we send a JSON payload to a push notification server (e.g. Apple's APNs), this payload is sent to the browser, then to our service worker, triggering events like onPush, notificationclick, enabling us to execute custom code on push notifications.

This was and still is how it works, but Declarative Web Push eliminates the last step of sending it to the service worker: the browser, instead, manages the notifications, shows it and brings native support to stuff like custom actions and navigation urls.

The service worker is still the only way to execute custom JS on notifications, Declarative Web Push supports the mutable field, enabling extra processing of the notification by the service worker.

We don't have this need for mutable notifications for now, this PR enables Declarative Web Push automatically by just adjusting the JSON payload and making sure it's backwards compatible to the Push API that we use for the rest of our users.


On a side note, it seems that Declarative Web Push doesn't support relative paths, nor local URLs, so developer experience is hindered. A WebKit bug has been opened.

TODOs

  • find a place for setting the app badge

Checklist

Are your changes backward compatible? Please answer below:

For example, a change is not backward compatible if you removed a GraphQL field or dropped a database column.
Yes, the service worker has been aligned to the new JSON payload.

On a scale of 1-10 how well and how have you QA'd this change and any features it might affect? Please answer below:
8.

macOS:

  • Safari: OK, works as intended, correct URL navigation
  • Firefox: OK, uses legacy Push API
  • Chrome/Brave: Shows "This site has been updated in the background" because of a Chromium bug with macOS 15+, not related to this PR, uses legacy Push API

iOS:

  • Safari: OK, works as intended, correct URL navigation
  • SN PWA: OK, works as intended, correct URL navigation

Linux:

  • Brave: OK, uses legacy Push API
  • Chrome: OK, uses legacy Push API
  • Firefox: OK, uses legacy Push API

For frontend changes: Tested on mobile, light and dark mode? Please answer below:
n/a

Did you introduce any new environment variables? If so, call them out explicitly here:
n/a

Footnotes

  1. Meet Declarative Web Push

@Soxasora Soxasora changed the title Declarative Web Push support, standardized JSON format Declarative Web Push support Jul 20, 2025
…med payload recognition on classic push notifications
@Soxasora Soxasora force-pushed the declarative_web_push branch from 47ac4f9 to 6db2717 Compare July 20, 2025 19:06
@Soxasora Soxasora marked this pull request as ready for review July 20, 2025 19:06
cursor[bot]

This comment was marked as outdated.

@Soxasora

This comment was marked as resolved.

@Soxasora Soxasora marked this pull request as draft July 20, 2025 19:32
@Soxasora Soxasora marked this pull request as ready for review July 27, 2025 09:52

// adapt declarative payload for legacy Push API
options = notification || {}
title = notification.title
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Service Worker Notification API Violation

The service worker incorrectly passes the entire notification object as options to self.registration.showNotification. This violates the Web Notifications API specification because the options object should not contain the title property (which is passed separately as the first argument) nor other declarative push properties (e.g., navigate, app_badge) unsupported by the legacy API. Furthermore, the destructuring of the push event payload can cause a TypeError if the payload is null or undefined, leading to a service worker crash.

Locations (1)

Fix in CursorFix in Web

Copy link
Member Author

@Soxasora Soxasora Jul 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the ultra-rare case of having a notification that's not sent by us, the try catch handles this

stacker.news/sw/index.js

Lines 78 to 94 in a4a0fdb

try {
payload = event.data?.json()
if (!payload) {
throw new Error('no payload in push event')
}
} catch (err) {
// we show a default nofication on any error because we *must* show a notification
// else the browser will show one for us or worse, remove our push subscription
return event.waitUntil(
self.registration.showNotification(
// TODO: funny message as easter egg?
// example: "dude i'm bugging, that's wild" from https://www.youtube.com/watch?v=QsQLIaKK2s0&t=176s but in wild west theme?
'something went wrong',
{ icon: '/icons/icon_x96.png' }
)
)
}

@huumn
Copy link
Member

huumn commented Jul 27, 2025

Correct me if I'm wrong: deploying this will remove badging from Apple devices?

@Soxasora
Copy link
Member Author

Soxasora commented Jul 27, 2025

Yeah, I admit it's not a great experience.

Then I remembered the new mutable property of these new declarative payloads.

The JSON also includes an entry specifying that "mutable” is true. Most declarative push messages are handled automatically, but this entry tells the browser that this notification needs to be processed by the service worker.
One new thing is that if the (push) event being dispatched originates from a push specified to be mutable, the event now has a copy of the notification proposed by the declarative JSON.

Maybe we can use it to easily set the badge like we usually do. 🤔 I'll take a look.

@Soxasora Soxasora marked this pull request as draft July 28, 2025 13:30
@Soxasora
Copy link
Member Author

Then I remembered the new mutable property of these new declarative payloads.

This defeats the purpose of declarative web push, passing the torch yet again to the legacy Push API to handle the notification, disabling all the benefits

Drafting until a declarative badge system is implemented in this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support Declarative Web Push
2 participants