← Back to Native mobile foundation team

Skill · Version 1.0.0

Notifications and deep links

Routes notification and link entry safely across app startup, authentication and unavailable resources.

mobile-notifications-deeplinks

Team members using this skill

See how it helps

When is it useful?

Use when tapping a notification opens the wrong screen or loses its destination.

What does it help you do?

  • Manages push registration and token rotation or removal.
  • Handles foreground, background and cold-start routing without duplicate navigation.
  • Validates links and restores destinations after login with meaningful back history.

Try asking this

Open an order notification while logged out, then sign in and handle an order that was deleted. Deliver the notification twice during cold start. Navigate once after authentication, then show a useful missing-order state instead of an empty or forbidden screen.

Detailed documentation

Read the skill’s original instructions, examples and checks in their authored language.

Details…Show less

Push Notifications & Deep Links

Deliver users from push notifications, Universal Links, Android App Links and custom URL schemes to the right screen safely across cold starts, background resumes and foreground sessions.

1. Push notification registration and token lifecycle

  • Contextual permission prompt: explain the value of notifications before calling requestPermissionsAsync() / UNUserNotificationCenter.requestAuthorization / Android 13+ POST_NOTIFICATIONS. If denied, keep the app fully functional and offer an unobtrusive Settings link in notification preferences.
  • Token lifecycle & multi-account isolation:
    • Register the device token (Expo Push Token, FCM registration token or APNs token) with the backend after authentication, associating it with the current user ID, device ID and app locale.
    • Subscribe to token rotation events (addPushTokenListener / FirebaseMessaging.onTokenRefresh) and update the backend immediately.
    • Unregister / disassociate the push token on sign-out (and clear delivered notifications from the OS tray + reset the app icon badge count) so a shared device never displays private notifications after logout.
  • Android 8+ Notification Channels: create explicit channels (setNotificationChannelAsync / NotificationChannel) with clear names and importance (MAX for urgent alerts, DEFAULT for messages, LOW for silent updates) before requesting a token or displaying a notification.

2. Handling all three app states without race conditions

Delivery State Required Behavior
1. Foreground (App Open) Configure foreground presentation (setNotificationHandler). Never hijack the screen with an automatic router.push while the user is typing; show an in-app toast/banner and invalidate/update the relevant query cache or unread badge.
2. Background / Inactive Tap Listen for notification response events (addNotificationResponseReceivedListener / onMessageOpenedApp) and navigate once the router is mounted.
3. Cold Start (App Terminated) Read the initial notification or launch URL (useLastNotificationResponse / getInitialMessage / getInitialURL), wait for auth state hydration and router readiness, and deduplicate by notification/message ID so hot reloads or re-renders do not navigate twice.

3. Universal Links, Android App Links and input validation

  • Prefer verified HTTPS links over custom schemes:
    • iOS Universal Links: configure Associated Domains (applinks:example.com) and .well-known/apple-app-site-association.
    • Android App Links: configure <intent-filter android:autoVerify="true"> with https scheme and .well-known/assetlinks.json.
    • Use custom schemes (myapp://) only as a fallback or for local development, as any app can claim an unverified custom scheme.
  • Treat incoming URLs and notification data payloads as untrusted input:
    • In Expo Router, use +native-intent.tsx (redirectSystemPath) wrapped in try/catch to normalize or reject malformed external URLs without crashing.
    • Whitelist allowed routes and validate parameter formats (UUID/slug); never pass raw deep-link params into SQL queries, file paths, WebView URLs or automatic state-mutating calls.
    • Use OAuth PKCE with ASWebAuthenticationSession / Custom Tabs and Universal Links instead of query-string tokens on custom schemes.

4. Auth gate, deleted resources and back-stack synthesis

  • Deferred post-login redirect: if a deep link or notification targets a protected route while signed out, stash the validated target path, route to sign-in, and redirect after login succeeds.
  • Graceful 403 / 404 screen state: if the linked item was deleted or belongs to another account, render a clear "Unavailable or access denied" state with a Home action.
  • Synthesize a back path: when a cold-start deep link opens a detail screen directly (/orders/123), ensure Back navigates to the parent list or Home instead of exiting unexpectedly.

Before you finish

  • Push tokens register on login, update on rotation, and unregister + clear tray/badge on logout.
  • Android channels exist; foreground, background-tap and cold-start flows are deduplicated.
  • Deep link URLs and notification payloads are validated as untrusted input with try/catch.
  • Unauthenticated links defer until login; missing/403 items and back-stack navigation work.

Included files 2

  • LICENSE.txt
  • SKILL.md