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.

  1. Deep links provide a way to link to a screen in your app. You’ll set up deep links by adding intent filters to the AndroidManifest.xml file.

     <intent-filter android:label="deep_linking_filter">
         <action android:name="android.intent.action.VIEW" />
         <category android:name="android.intent.category.DEFAULT" />
         <category android:name="android.intent.category.BROWSABLE" />
         <!-- Accepts URIs that begin with "amiapp://home" -->
         <data
             android:host="home"
             android:scheme="amiapp" />
     </intent-filter>
  2. Now you’re ready to handle deep links. In your App.js file or anywhere you handle navigation, you’ll add code that looks like this.

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

After you set up intent filters, you can test your implementation with the Rich Push editor or the payloads included for Testing push notifications.

Push Click Behavior

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

const config: CioConfig = {
  cdpApiKey: 'cdp_api_key',
  region: CioRegion.US,
  inApp: {
    siteId: 'site_id',
  },
  push: {
    android: {
      pushClickBehavior: PushClickBehaviorAndroid.ActivityPreventRestart
    }
  }
};
CustomerIO.initialize(config)

The available options are:

  • ActivityPreventRestart (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.

  • ActivityNoFlags: 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.

  • ResetTaskStack: 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.

Deep links let you open a specific page in your app instead of opening the device’s web browser. Want to open a screen in your app or perform an action when a push notification or in-app button is clicked? Deep links work great for this!

Setup deep linking in your app. There are two ways to do this; you can do both if you want.

  • Universal Links: universal links let you open your mobile app instead of a web browser when someone interacts with a URL on your website. For example: https://your-social-media-app.com/profile?username=dana—notice how this URL is the same format as a webpage.
  • App scheme: app scheme deep links are quick and easy to setup. Example of an app scheme deep link: your-social-media-app://profile?username=dana. Notice how this URL is not a URL that could show a webpage if your mobile app is not installed.

Universal Links provide a fallback for links if your audience doesn’t have your app installed, but they take longer to set up than App Scheme deep links. App Scheme links are easier to set up but won’t work if your audience doesn’t have your app installed.

After you set up push notifications you can enable deep links in rich push notifications. 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.

However, before you can do this, you need to set up your app link scheme for iOS. Learn more about URL schemes for iOS apps.

Closed-app deep links on AppDelegate-only hosts

On AppDelegate-only iOS hosts, deep link click events won’t fire when your app is closed. See our troubleshooting section for a workaround.

This limitation does not apply to hosts that have adopted the UIScene lifecycle with React Native 0.88 or newer and customerio-reactnative 6.9.0 or newer. On supported scene hosts, cold-start destinations are buffered and delivered to your Linking listener once React Native is ready. See Set up deep links for UIScene hosts below.

  1. Open your project in Xcode and select your root project in the Project Navigator.

  2. Go to the Info tab.

  3. Scroll down to the options in the Info tab and expand URL Types.

  4. Click to add a new, untitled schema.

  5. Under Identifier and URL Schemes, add the name of your schema.

    set up your deep link scheme in xcode
  6. Open your AppDelegate.swift file and add the code below. Note that the Customer.io SDK automatically forwards deep links from Customer.io push notifications to the application(:open:options:) method. For push notifications from other providers, you still need to handle deep links manually in the userNotificationCenter(didReceive:withCompletionHandler:) method.

     import UIKit
     import CioMessagingPushAPN
     import React
    
     @main
     class AppDelegateWithCioIntegration: CioAppDelegateWrapper<AppDelegate> {}
    
     class AppDelegate: UIResponder, UIApplicationDelegate {
         
         func application(_ app: UIApplication, 
                         open url: URL, 
                         options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
             // Handle deep links
             return RCTLinkingManager.application(app, open: url, options: options)
         }
     }
  7. Now you’re ready to handle deep links. In your App.js file or anywhere you handle navigation, you’ll add code that looks like this.

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

Follow React Native’s documentation to implement Universal Links in your app.

The AppDelegate setup above is the no-change path: if your app still uses the AppDelegate lifecycle, you don’t need anything here. These steps apply once your host adopts the UIScene lifecycle (its Info.plist declares a UIApplicationSceneManifest and it owns a SceneDelegate).

Use React Native 0.88 or newer with customerio-reactnative 6.10.0 or newer for acknowledged scene routing. Version 6.9.0 remains compatible through the earlier Linking path. React Native 0.87 and earlier don’t expose the scene URL APIs that the Customer.io bridge requires. Keep the AppDelegate path until React Native 0.88 is available as a stable release. For the full cross-platform walkthrough, see the iOS 27 UIScene migration guide.

  1. Install acknowledged deep-link routing before React Native starts. In your SceneDelegate, call this first in scene(_:willConnectTo:options:), before you start the React Native factory:

    import customerio_reactnative
    
    NativeCustomerIO.configureAcknowledgedSceneDeepLinkRouting()
  2. Pass cold connection options when React Native starts. This preserves ordinary app-scheme URLs and universal links delivered when the app is terminated:

    reactNativeFactory?.startReactNative(
      withModuleName: "YourApp",
      in: window,
      connectionOptions: connectionOptions
    )

    If you use Customer.io Live Activities, use the launchOptions call in the Live Notifications setup instead. That helper attributes a Live Activity tap and preserves ordinary connection URLs for React Native.

  3. Forward warm scene URLs and universal links to React Native. These callbacks preserve your existing app links independently of Customer.io destinations:

    import React
    
    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
      RCTLinkingManager.scene(scene, openURLContexts: URLContexts)
    }
    
    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
      RCTLinkingManager.scene(scene, continue: userActivity)
    }

    If you use Customer.io Live Activities, use the handleAndRouteWidgetUrl URL-context callback from the Live Notifications setup instead. It passes ordinary URLs through to React Native too.

  4. Register the Customer.io deep-link handler before CustomerIO.initialize. Return true after your router accepts a destination. Return false to use the native fallback.

    import { useEffect } from 'react';
    import { CustomerIO, CioRegion } from 'customerio-reactnative';
    
    useEffect(() => {
      const subscription = CustomerIO.setDeepLinkHandler(async (url) => {
        if (!canRouteInApp(url)) {
          return false;
        }
    
        await routeInApp(url);
        return true;
      });
    
      CustomerIO.initialize({
        cdpApiKey: 'your-cdp-api-key',
        region: CioRegion.US,
      });
    
      return () => subscription.remove();
    }, []);

On a scene host, Customer.io push, in-app, and inbox destinations are buffered during cold launch until the acknowledged handler registers. The handler has ten seconds to return a result. Returning false, throwing, rejecting, missing registration, or timing out makes the SDK try the host AppDelegate. It then sends an app-owned custom scheme to React Native Linking, or opens another URL through the system. Keep a Linking listener when your handler might decline an app-owned custom scheme. A handler that routes after the timeout can cause a second navigation because its late result can’t cancel a fallback that already ran.

Backward-compatible Linking path

Version 6.9.0 scene integrations can keep NativeCustomerIO.configureSceneDeepLinkRouting() and a Linking listener registered before CustomerIO.initialize. That path still buffers cold URLs, but a Linking event can’t report whether JavaScript handled it. The SDK therefore can’t safely fall back after publishing the event. Prefer the acknowledged handler for new integrations.

The supported scope is one simultaneous window scene. Multiple simultaneous React Native window scenes are outside this release.

Updated August 28, 2026