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.
Add the package
Terminal npm install @ondropp/dropplink-react-native cd ios && pod installAutolinking registers the module on both platforms. Rebuild the app with
npx react-native run-androidorrun-ios: a Metro reload doesn't pick up native code.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_secretIn CI, write the
.envfrom your secrets before building.Initialize at the top of index.js
Call
initializebeforeAppRegistry.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);initializereturns right away. The native SDK makes the network call in the background.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
Linkingyourself, callDropplink.handleDeepLink(url)from there instead oflistenToLinking().Set up App Links and Universal Links
Finish the deep link setup for your link domain. On Android, the intent filter goes on
MainActivityinandroid/app/src/main/AndroidManifest.xml, and React Native'sLinkingalready reads it.On iOS, add the Associated Domains capability, and make sure
AppDelegatepasses links toRCTLinkingManager(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#
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 trackingTrack 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#
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.