Radical
How-to guides

Handle notification taps

Own notification navigation in the app scope.

Mount the watcher

In the root component's mount effect, use useRouter() and the example imports:

const fiber = Effect.runFork(
  Effect.scoped(
    watchNotificationNavigation({ appId: config.appId, push: true })
  ).pipe(
    Effect.provide(
      Layer.mergeAll(
        BridgeLive,
        NotificationSnapshotsLive.pipe(Layer.provide(BridgeLive)),
        tanstackStartAdapterLayer(router)
      )
    )
  )
);
// Effect cleanup:
return () => {
  Effect.runFork(Fiber.interrupt(fiber));
};

Keep the watcher mounted across screen changes.

Set destinations

Use app-relative routes:

SourceFieldWatcher option
LocalrouteOmit push: true if local-only
Pushdata.routepush: true
OfflineMark route offline; visit exact URL after worker activationMatch query strings

Verify taps

Test invalid routes, online taps, and offline taps to visited URLs.

You see valid destinations open; invalid routes are ignored.

Validated taps select a stored document or router navigation
flowchart TD
  Local[LocalNotifications action] --> Queue[App-scoped watcher queue]
  Push[FirebaseMessaging action when push: true] --> Queue
  Browser[radical-notification browser event] --> Queue
  Queue --> Valid{actionId is tap and route is valid?}
  Valid -->|No| Ignore[Ignore event]
  Valid -->|Yes| Network{getNetworkStatus}
  Network -->|Connected| Router[Adapter navigation.navigate]
  Network -->|Disconnected or status failure| Cache{Exact URL in app snapshot cache?}
  Cache -->|Yes| Document[location.assign: full document navigation]
  Cache -->|No or restore error| Router
  Router -->|Failure| Error[onError callback; watcher continues]

On this page