I'm working on a Next.js PWA using Firebase Cloud Messaging (FCM). I have implemented a service worker to handle background push notifications, and I'm experiencing inconsistent behavior across platforms when the user clicks on the notification:
What works:
iOS (Safari installed PWA): notification opens the correct deep link inside the app.
Web (desktop): notification opens the expected URL as well.
What fails (Android):
If the PWA is in foreground, the notification works as expected — the app navigates to the conversation screen.
If the PWA is in the background, tapping the notification does nothing.
If the PWA is closed, tapping the notification also does nothing — the app doesn't open at all.
However, if the PWA was in the background and I manually open it after tapping the notification, it does redirect correctly to the conversation. So the logic is working — it's just not opening the app.
My Setup firebase-messaging-sw.js:
self.addEventListener("notificationclick", (event) => {
event.preventDefault();
console.log("[SW] notificationclick received", event.notification.data);
const url = event.notification.data?.url || "/";
console.log("[SW] will open URL:", url);
event.notification.close();
event.waitUntil(
clients
.matchAll({ type: "window", includeUncontrolled: true })
.then((windowClients) => {
console.log("[SW] windowClients:", windowClients.length);
for (const client of windowClients) {
// Si encuentras un cliente con tu dominio, úsalo
if (client.url.startsWith(self.location.origin)) {
console.log("[SW] navigating existing client:", client.url);
return client
.navigate(url)
.then((navResult) => {
console.log("[SW] navigate() result:", navResult);
return client.focus();
});
}
}
// Si no hay ninguno, abre uno nuevo
console.log("[SW] no matching client, openWindow:", url);
return clients.openWindow(url);
})
.catch((err) => {
console.error("[SW] error in notificationclick handler:", err);
// como fallback abre siempre algo
return clients.openWindow(url);
})
);
});
In the app (Next.js), I handle redirection with a hook that is called on root layout:
// hooks/useRedirectFromNotification.ts
useEffect(() => {
const qs = new URLSearchParams(window.location.search);
const to = qs.get("redirect");
if (to) {
setTimeout(() => router.replace(to), 300);
window.history.replaceState({}, "", window.location.pathname);
}
}, []);
useEffect(() => {
if (!("serviceWorker" in navigator)) return;
const handler = (e: MessageEvent) => {
const data = e.data || {};
if (data.type === "NAVIGATE" && data.url) {
router.push(data.url);
}
};
navigator.serviceWorker.addEventListener("message", handler);
return () => navigator.serviceWorker.removeEventListener("message", handler);
}, []);
My manifest.json includes:
{
"start_url": "/",
"scope": "/",
"display": "standalone",
"gcm_sender_id": "726337043778"
}
What I tried:
Using .navigate(url) directly — doesn’t work in Android.
Only using .openWindow() — opens in Chrome browser, not PWA.
Fallback with /?redirect=... and postMessage — logic works but app doesn’t open.
Setting display: "standalone" and proper start_url, scope, theme_color in manifest.json.
My question:
- Why does clicking the notification not open the PWA on Android when it's in the background or closed?
- Is there a reliable way to force the Android PWA (installed via Chrome) to launch from a push notification?
Any insight or workaround would be appreciated.Thank you in advance!