Skip to content

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 call showNotification so the user sees a message.
  • notificationclick — Fires when the user interacts with the notification; you typically focus an existing tab or open data.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:

  1. Parse event.data as JSON when possible; fall back to plain text as the title.
  2. Show a notification with title, body, icon, badge, tag, and data.url (default /).
  3. 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.

  • 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 default serviceWorkerPath.
  • Or put it under another path and pass serviceWorkerPath: '/assets/gesher-sw.js' (and ensure that URL returns Content-Type: application/javascript).

The file must be same-origin (or a scope your registration allows).

Scope considerations

  • serviceWorkerScope defaults 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.

Gesher — managed web push by Otomator