Service Worker Guide
Web push requires a service worker on your own origin. The worker receives push events from the browser, shows notifications, and handles clicks (for example opening a URL).
What it does and why it’s needed
push— Fires when Gesher (via the browser’s push service) delivers a payload. You callshowNotificationso the user sees a message.notificationclick— Fires when the user interacts with the notification; you typically focus an existing tab or opendata.url.
Without a worker (and without HTTPS on a real origin), push cannot work.
Default gesher-sw.js from the SDK
The published template lives in the package as gesher-sw.js and is exposed as import path @gesher/sdk/sw.
Behavior:
- Parse
event.dataas JSON when possible; fall back to plain text as the title. - Show a notification with
title,body,icon,badge,tag, anddata.url(default/). - On click, close the notification, prefer focusing a window that already matches the URL, otherwise
openWindow(url).
Conceptually:
js
self.addEventListener('push', function (event) {
if (!event.data) return
let payload
try {
payload = event.data.json()
} catch {
payload = { title: event.data.text() }
}
const title = payload.title || 'New notification'
const options = {
body: payload.body || '',
icon: payload.icon || undefined,
badge: payload.badge || undefined,
tag: payload.tag || undefined,
data: { url: payload.url || '/' },
}
event.waitUntil(self.registration.showNotification(title, options))
})
self.addEventListener('notificationclick', function (event) {
event.notification.close()
const url = event.notification.data?.url || '/'
event.waitUntil(
clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (clientList) {
for (const client of clientList) {
if (client.url === url && 'focus' in client) {
return client.focus()
}
}
if (clients.openWindow) {
return clients.openWindow(url)
}
})
)
})Customization
RTL and Hebrew
showNotification accepts a dir option: 'auto' | 'ltr' | 'rtl'. For Hebrew-first apps:
js
const options = {
body: payload.body || '',
dir: 'rtl',
lang: 'he',
data: { url: payload.url || '/' },
}You can also set icon / badge to assets tuned for RTL layouts.
Icons and deep links
icon— Large icon in the notification UI.badge— Monochrome badge where supported.data.url— Use absolute URLs (https://app.example.com/path) if clicks should open a specific host or path; relative paths resolve against the worker’s scope.
Tag and replacement
Map Gesher’s tag field into options.tag so new notifications replace older ones with the same tag (browser-dependent).
Where to place the file
- Serve from your site root, e.g.
/gesher-sw.js, which matches the SDK defaultserviceWorkerPath. - Or put it under another path and pass
serviceWorkerPath: '/assets/gesher-sw.js'(and ensure that URL returnsContent-Type: application/javascript).
The file must be same-origin (or a scope your registration allows).
Scope considerations
serviceWorkerScopedefaults to/so the worker can receive push for the whole app.- A narrower scope (e.g.
/app/) only makes sense if all push-enabled pages live under that path. - Changing scope after users have registered may require a new registration strategy; prefer
/unless you have a reason not to.