DropplinkDocsPowered bydropp.

Platforms

React Native

Add the React Native module and handle deep links with a hook.

React Native 0.76 and up, with the New or the old Architecture. The module wraps the native Android and iOS SDKs, which do the work. It runs on Android 7.0 (API 24) and up, and on React Native's minimum iOS version.

Using Expo?

The module has native code, so it works in a development build (npx expo prebuild), not in Expo Go.

  1. Add the package

    Terminal
    npm install @ondropp/dropplink-react-native
    cd ios && pod install

    Autolinking registers the module on both platforms. Rebuild the app with npx react-native run-android or run-ios: a Metro reload doesn't pick up native code.

  2. Add your SDK keys

    Each platform has its own key in Settings → SDK keys. Don't commit the secrets in your JavaScript. Load them from build configuration instead, for example with react-native-config and a git-ignored .env:

    .env
    DROPPLINK_ANDROID_KEY_ID=ak_3f9c1e2a7b6d4c80
    DROPPLINK_ANDROID_SECRET=your_android_secret
    DROPPLINK_IOS_KEY_ID=ak_9b2d4f6a8c0e1357
    DROPPLINK_IOS_SECRET=your_ios_secret

    In CI, write the .env from your secrets before building.

  3. Initialize at the top of index.js

    Call initialize before AppRegistry.registerComponent, so the install is reported even if someone leaves the first screen quickly:

    index.js
    import { AppRegistry, Platform } from 'react-native';
    import Config from 'react-native-config';
    import { Dropplink } from '@ondropp/dropplink-react-native';
    import App from './App';
     
    Dropplink.initialize({
      keyId: Platform.select({ ios: Config.DROPPLINK_IOS_KEY_ID, default: Config.DROPPLINK_ANDROID_KEY_ID }),
      secret: Platform.select({ ios: Config.DROPPLINK_IOS_SECRET, default: Config.DROPPLINK_ANDROID_SECRET }),
      debug: __DEV__,
    }).catch((e) => console.warn('Dropplink.initialize failed', e));
     
    AppRegistry.registerComponent('MyApp', () => App);

    initialize returns right away. The native SDK makes the network call in the background.

  4. Listen for deep links

    Add the listener in the component that can navigate, and forward the URLs your app is opened with:

    App.tsx
    import { useEffect } from 'react';
    import { Dropplink, useDropplinkDeepLink } from '@ondropp/dropplink-react-native';
     
    export default function App() {
      // Every Dropplink deep link while App is mounted.
      useDropplinkDeepLink((link) => {
        // link.path = "/product/123", link.params = { variant: "red" }
        navigation.navigate(routeFor(link.path), link.params);
      });
     
      // Passes the launch URL and later opens from React Native's Linking to the SDK.
      useEffect(() => Dropplink.listenToLinking(), []);
     
      return <Navigation />;
    }

    If a link arrives before any listener exists, which is common for the deferred link, the SDK holds it and delivers it when the first listener is added. If you already handle Linking yourself, call Dropplink.handleDeepLink(url) from there instead of listenToLinking().

  5. Set up App Links and Universal Links

    Finish the deep link setup for your link domain. On Android, the intent filter goes on MainActivity in android/app/src/main/AndroidManifest.xml, and React Native's Linking already reads it.

    On iOS, add the Associated Domains capability, and make sure AppDelegate passes links to RCTLinkingManager (new projects already do):

    AppDelegate.swift
    func application(_ application: UIApplication, continue userActivity: NSUserActivity,
                     restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
      RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
    }
     
    func application(_ app: UIApplication, open url: URL,
                     options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
      RCTLinkingManager.application(app, open: url, options: options)
    }

That's the whole integration. Next, test an install before you release.

Using React Navigation's linking config?

Don't map your link domain in its prefixes. The URL holds the link's code, like /diwali, not a screen. Route from useDropplinkDeepLink using link.path instead.

Events and users#

Anywhere in your app
Dropplink.track('add_to_cart', { props: { sku: 'TSHIRT-M', qty: 1 } });
Dropplink.track('purchase', { revenue: { amount: 499, currency: 'INR', orderId: 'A1023' } });
 
Dropplink.identify(user.id, { email: user.email, phone: user.phone }); // after sign-up or login
Dropplink.logout();
 
Dropplink.setTrackingEnabled(false); // the user declined tracking

Track the funnel with track, and call identify with your own user ID as soon as someone signs up or logs in (and on every launch after that), so their events and revenue follow them. Events are saved on the phone and sent in batches, so you don't need to handle networking or being offline. Send purchases that should pay creators from your server: revenue tracked in the app is shown as app-reported.

More: Tracking events · Users and revenue credit

API#

@ondropp/dropplink-react-native
Dropplink.initialize(config: { keyId: string; secret: string; debug?: boolean }): Promise<void>
Dropplink.addDeepLinkListener(listener: (link: DeepLink) => void): { remove(): void }
Dropplink.handleDeepLink(url: string): Promise<void>
Dropplink.getAttribution(): Promise<Attribution | null>   // null before the first successful launch report
Dropplink.getInstallId(): Promise<string | null>          // null before initialize
Dropplink.listenToLinking(): () => void                   // returns an unsubscribe function
Dropplink.track(name: string, options?: { props?: EventProps; revenue?: Revenue }): void
Dropplink.identify(userId: string, traits?: { email?: string; phone?: string }): void
Dropplink.logout(): void
Dropplink.setTrackingEnabled(enabled: boolean): void
Dropplink.isTrackingEnabled(): Promise<boolean>           // false if the module isn't linked
useDropplinkDeepLink(handler: (link: DeepLink) => void): void
 
type DeepLink = {
  path: string;                     // "/product/123"
  params: Record<string, string>;
  url: string | null;               // the opened URL; null for a deferred link
  isDeferred: boolean;              // true only for the link delivered after a fresh install
};
 
type EventProps = Record<string, string | number | boolean | null>;
type Revenue = { amount: number; currency: string; orderId?: string };
 
type Attribution = {
  type: 'attributed' | 'organic';
  method: 'install_referrer' | 'fingerprint' | 'none';
  clickId?: string; linkId?: string; campaignId?: string; creatorId?: string;
  source?: string;                  // the channel, e.g. "instagram"
  confidence?: number;
  attributedAt?: string;
};

Promises resolve once the call reaches the native SDK, without waiting for the network. They reject only if the native module isn't linked, or, on Android in a debug build, if a key ID or secret is empty or malformed. Network and server errors never reach JavaScript: the native SDK retries them and logs them when debug is on.

Keep a single deep link listener. Adding the hook in two mounted components delivers each link twice.

Something unclear? Tell us.Powered bydropp.