Angular push notifications with SwPush
Angular has push built in: the @angular/service-worker package’s SwPush service subscribes users and its ngsw-worker.js displays notifications from a specific payload format. Here’s the whole flow and the gotchas.
- Add the service worker with ng add @angular/pwa; SwPush only works in production builds where the worker is enabled.
- Call swPush.requestSubscription({ serverPublicKey }) from a click handler and post the result to your server.
- ngsw-worker.js shows a notification only when the payload has a “notification” object.
- Use data.onActionClick to control what a click opens.
1. Add the service worker
ng add @angular/pwa
// app.config.ts (standalone apps)
import { provideServiceWorker } from '@angular/service-worker';
import { isDevMode } from '@angular/core';
export const appConfig = { providers: [
provideServiceWorker('ngsw-worker.js', { enabled: !isDevMode(), registrationStrategy: 'registerWhenStable:30000' })
]};Because the worker is disabled in dev mode, test push with ng build and a static server over HTTPS or on localhost.
2. Subscribe with SwPush
import { Component, inject } from '@angular/core';
import { SwPush } from '@angular/service-worker';
import { HttpClient } from '@angular/common/http';
@Component({ selector: 'app-notify', template: `<button (click)="enable()" [disabled]="!swPush.isEnabled">Get notifications</button>` })
export class NotifyComponent {
swPush = inject(SwPush); http = inject(HttpClient);
async enable() {
const sub = await this.swPush.requestSubscription({ serverPublicKey: 'YOUR_VAPID_PUBLIC_KEY' });
this.http.post('/api/push/subscribe', sub).subscribe();
}
}3. The payload ngsw expects
{
"notification": {
"title": "Build #482 passed",
"body": "main → production in 3m 12s",
"icon": "/icons/icon-192x192.png",
"actions": [{ "action": "logs", "title": "View logs" }],
"data": {
"onActionClick": {
"default": { "operation": "navigateLastFocusedOrOpen", "url": "/builds/482" },
"logs": { "operation": "openWindow", "url": "/builds/482/logs" }
}
}
}
}Send that JSON from any Web Push library (Node, Python, Go). Without a top-level notification key, ngsw won’t display anything — it only forwards the message to open pages via swPush.messages.
4. Listen in the app
this.swPush.messages.subscribe(msg => console.log('push while open', msg));
this.swPush.notificationClicks.subscribe(({ action, notification }) => { /* analytics */ });Common pitfalls
swPush.isEnabledis false in dev mode or on HTTP — that’s expected.- Ask on a click; Chrome and Firefox quiet or block prompts without a gesture.
- Angular’s data groups don’t matter for push, but a stale
ngsw.jsonafter deploy can delay worker updates — check/ngsw/state. - iPhone requires a Home Screen install on iOS 16.4+; the manifest from
ng add @angular/pwahelps.
Show the right state and allow opt-out
// subscription$ emits the current PushSubscription or null
this.swPush.subscription.subscribe(sub => this.enabled = !!sub);
async disable() {
const sub = await firstValueFrom(this.swPush.subscription);
if (sub) await firstValueFrom(this.http.post('/api/push/unsubscribe', { endpoint: sub.endpoint }));
await this.swPush.unsubscribe();
}Hide the button entirely when swPush.isEnabled is false or Notification.permission is denied, and explain how to re-enable in browser settings instead.
Sending the ngsw payload from Node
const payload = JSON.stringify({ notification: {
title: 'Build #482 passed', body: 'main → production in 3m 12s',
data: { onActionClick: { default: { operation: 'navigateLastFocusedOrOpen', url: '/builds/482' } } }
}});
await webpush.sendNotification(subscription, payload, { TTL: 3600 });Any library works — the key point is the top-level notification object.
Ionic and Capacitor apps
Autocomplete suggests many people search “Angular Capacitor push notifications”. Inside a Capacitor native shell, notifications use native APIs (APNs, FCM) through a Capacitor plugin, not the web Push API. The SwPush approach applies to the web/PWA build of the same app. Plan both if you ship to the App Store and the web.
Using a hosted service with Angular
One scope can only have one service worker. If you want a hosted service instead of your own sender, you can keep Angular’s caching worker and add the service’s push handling to it, or drop ngsw if you don’t need offline caching. With Pusharoo, put the snippet in src/index.html and either upload sw.js (if you don’t use ngsw) or create a small custom worker that loads both:
// src/custom-sw.js — list it in angular.json "assets" and register it instead of ngsw-worker.js
importScripts('./ngsw-worker.js');
self.PUSHAROO_SITE = 'pk_your_site_key';
importScripts('https://pusharoo.com/sw.js');Point the snippet at it with data-sw="/custom-sw.js" and test both caching and push in a production build.
Questions
Why is SwPush not working in development?
The Angular service worker is only enabled in production builds by default; swPush.isEnabled is false otherwise.
Do I need Firebase for Angular push notifications?
No. SwPush uses the standard Push API with your VAPID public key. Firebase is optional.
Why doesn’t my notification show?
ngsw needs a payload with a top-level “notification” object. Also check OS notification settings and Focus modes.
Can I use Firebase Cloud Messaging with Angular instead of SwPush?
Yes. AngularFire or the Firebase JS SDK can get an FCM token and display messages through Firebase’s own service worker. That adds a Google project and replaces ngsw for push. For web-only apps, SwPush with standard VAPID keys is simpler and works with any Web Push sending library.
How do I test Angular push notifications locally?
Build for production (ng build), serve the dist folder on localhost with any static server, subscribe in Chrome, then send a test with a Web Push library or use DevTools → Application → Service workers → Push with a JSON payload containing a notification object.
Keep reading
The Web Push API in JavaScript: a working example
Register a worker, ask on click, subscribe with VAPID, store the subscription and send — complete code.
Service worker push events, done right
push and notificationclick handlers, focusing open tabs, action buttons, subscription changes and pitfalls.
Web push notifications in React and Next.js
Service worker in public/, a subscribe hook, a route handler with web-push, env keys and iPhone notes.
Push notifications in Vue 3 and Nuxt
A usePush composable, sw.js in public/, vite-plugin-pwa injectManifest and Nuxt head config.
More in Developers: VAPID keys for web push, explained · The Web Push protocol, explained · Web push notifications with Node.js · Web push notifications in PHP · Laravel browser push notifications (no Firebase) · Declarative Web Push, explained · Firebase web push vs. standard web push · Web push in Python with pywebpush · Django push notifications to the browser · Web push notifications in Ruby on Rails · Web push in Go with webpush-go · Push notifications in SvelteKit · All guides
Free for life. No limits.
Unlimited subscribers, campaigns and automations. No badges. About five minutes to install.