GitHub project

Deep links

How it works

Deep links are the links that directs users to a specific location within a mobile app. When you set up your notification, you can set a “deep link.” When your audience taps the notification, the SDK will route users to the right place.

Deep links help make your message meaningful, with a call to action that makes it easier, and more likely, for your audience to follow. For example, if you send a push notification about a sale, you can send a deep link that takes your audience directly to the sale page in your app.

However, to make deep links work, you’ll have to handle them in your app. We’ve provided instructions below to handle deep links in both Android and iOS versions of your app.

There are a number of ways to enable deep links. Our example below uses @react-navigation with a config and prefix to automatically set paths. The paths are the values you’d use in your push payload to send a link.

import { NavigationContainer } from '@react-navigation/native';
const config = {
 screens: {
   Home: {
     path: 'home/:id?',
     parse: {
       id: (id: String) => `${id}`,
     },
   },
 }
};
const linking = {
    prefixes: ['myapp://'],
    config
};

iOS scene lifecycle (Expo SDK 58+)

Expo SDK 58 is pre-release

Stable Expo SDK 57 apps keep the AppDelegate path. Apply this section after Expo SDK 58 is available as a stable release.

If you build with Expo SDK 58 or newer, Expo generates a UIScene-based iOS project. On this path, use customerio-expo-plugin 3.9.0 or newer with its required customerio-reactnative 6.9.0 peer. Below Expo SDK 58, the plugin keeps the AppDelegate path and nothing here changes.

Expo owns the scene lifecycle; the plugin configures the native Customer.io bridge to match it. For the full cross-SDK picture, see the iOS 27 UIScene migration guide.

The readiness step depends on how you initialize Customer.io:

  • With native auto-initialization, register your router first, then call CustomerIO.setDeepLinkRoutingReady().
  • With JavaScript initialization, register your router first, then call CustomerIO.initialize(...). Initialization marks scene routing ready automatically, so don’t also call setDeepLinkRoutingReady().

For native auto-initialization:

  1. Register your React Native Linking URL listener.
  2. Call CustomerIO.setDeepLinkRoutingReady().
import { Linking } from 'react-native';
import { CustomerIO } from 'customerio-reactnative';

// Register your URL listener first...
Linking.addEventListener('url', ({ url }) => {
  // route the URL with your navigator
});

// ...then signal that URL handling is ready.
CustomerIO.setDeepLinkRoutingReady();
  • Apps without Expo Router register their Linking listener first, then call setDeepLinkRoutingReady().
  • Expo Router apps must not add a second navigating Linking listener. Expo Router already owns that subscription. Call setDeepLinkRoutingReady() once your router is ready.

Cold-start notification destinations are buffered and delivered to your Linking listener as url events after you signal readiness. They aren’t returned by Linking.getInitialURL(). If readiness isn’t signaled within ten seconds, Customer.io opens the destination through the system as a fallback.

Note

handleDeeplinkInKilledState is the legacy AppDelegate workaround for Expo SDK 53–57. It isn’t injected for the Expo SDK 58+ scene lifecycle because scene routing replaces it. See troubleshooting.

To set up deep links in Android, you need to declare your intentFilters in your app.json or app.config.js.

{
    "expo": {
      ...
      "android": {
        ...
        "intentFilters": [
          {
            "action": "VIEW",
            "data": [
              {
                "scheme": "<YourScheme>",
                "host": "<YourHost>"
              }
            ],
            "category": [
              "BROWSABLE",
              "DEFAULT"
            ]
          }
        ]
      }
      ...
    }
}

Push Click Behavior

The android.pushClickBehavior config option controls how your app behaves when your audience taps push notifications. The SDK automatically tracks Opened metrics for all options.

{
   "plugins": [
      [
         "customerio-expo-plugin",
         {
            "android": {
               "pushClickBehavior": "ActivityPreventRestart"
            }
         }
      ]
   ]
}

The available options are:

  • ACTIVITY_PREVENT_RESTART (Default): If your app is already in the foreground, the SDK will not re-create your app when your audience clicks a push notification. Instead, the SDK will reuse the existing activity. If your app is not in the foreground, we’ll launch a new instance of your deep-linked activity. We recommend that you use this setting if your app has screens that your audience shouldn’t navigate away from—like a shopping cart screen.

  • ACTIVITY_NO_FLAGS: If your app is in the foreground, the SDK will re-create your app when your audience clicks a notification. The activity is added on top of the app’s existing navigation stack, so if your audience tries to go back, they will go back to where they previously were.

  • RESET_TASK_STACK: No matter what state your app is in (foreground, background, killed), the SDK will re-create your app when your audience clicks a push notification. Whether your app is in the foreground or background, the state of your app will be killed so your audience cannot go back to the previous screen if they press the back button.

To support iOS, you need to declare your deep link scheme in your Expo project’s app.json or app.config.js file.

Apple has a few different ways to set up deep links. Learn more about URL schemes for iOS apps.

{
   "expo": {
      ...
      "ios": {
      ...
         "infoPlist": {
            "CFBundleURLTypes": [
               {
               "CFBundleURLSchemes": ["<YourScheme>"]
               }
            ]
         }
      }
      ...
   }
}

Closed-app deep links on iOS

On the AppDelegate path (Expo SDK 53–57, or projects that haven’t adopted the scene lifecycle), deep link click events won’t fire when your app is closed. See our troubleshooting section for the workaround.

This no longer applies to Expo SDK 58+ scene apps using customerio-expo-plugin 3.9.0 with customerio-reactnative 6.9.0. Cold-start destinations are delivered to your Linking listener after scene routing is ready. See iOS scene lifecycle.

Updated September 1, 2026