React Native SDK

Install and integrate the LinkTrail React Native SDK — linktrail-react-native, a New Architecture TurboModule.

A New Architecture TurboModule that wraps the native iOS and Android SDKs behind one JS API (LinkTrail) · package linktrail-react-native · React Native 0.76+, iOS 15.1+, Android minSdk 26.

Install

npm install linktrail-react-native

The native SDKs resolve automatically — LinkTrailSDK from CocoaPods and io.linktrail:sdk from Maven Central — so there's nothing else to add. On iOS run cd ios && pod install; on Android just make sure minSdkVersion is 26 or higher.

Expo needs a development build (not Expo Go — this is a native TurboModule). Declare the deep-link config in app.json too, then run npx expo prebuild.
app.jsonjson
{
  "expo": {
    "plugins": [["expo-build-properties", { "android": { "minSdkVersion": 26 } }]],
    "ios": { "associatedDomains": ["applinks:yourapp.linktrail.io"] },
    "android": {
      "intentFilters": [{
        "autoVerify": true,
        "action": "VIEW",
        "data": [{ "scheme": "https", "host": "yourapp.linktrail.io" }],
        "category": ["BROWSABLE", "DEFAULT"]
      }]
    }
  }
}

Integrate

import LinkTrail from 'linktrail-react-native';

// At app launch. The API key is required; the install is tracked automatically.
await LinkTrail.configure('lt_live_...');

// One listener handles first-launch (deferred) AND re-engagement links:
LinkTrail.onLink((link, source) => {
  router.navigate(link.path, link.customData);
});

LinkTrail.onError((error) => console.warn('[LinkTrail] ' + error.code + ': ' + error.message));

Incoming links (Universal Links / App Links / custom schemes) are picked up from React Native's Linking API automatically. One iOS requirement: your AppDelegate must forward URLs to RCTLinkingManager — and the continue userActivity override below is NOT in the stock React Native template, so Universal Links silently never arrive without it:

ios/YourApp/AppDelegate.swiftswift
import React

// Custom-scheme URLs (yourapp://…):
func application(_ app: UIApplication, open url: URL,
                 options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
  RCTLinkingManager.application(app, open: url, options: options)
}

// Universal Links (https://yourapp.linktrail.io/…):
func application(_ application: UIApplication, continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
  RCTLinkingManager.application(application, continue: userActivity,
                                restorationHandler: restorationHandler)
}

Listeners return a subscription — call .remove() when your component unmounts. onLink also fires immediately if a deferred link already resolved this session, so registration order vs. the auto-tracked install doesn't matter.

More

// Custom + revenue events:
await LinkTrail.trackEvent('purchase', { value: 59.99, currency: 'USD' });

// Cached results:
const attribution = await LinkTrail.getLastAttribution();
const lastLink = await LinkTrail.getLastDeepLink();

// Consent-gated install (defer the auto-track, then call it manually):
await LinkTrail.configure('lt_live_...', { autoTrackInstall: false });
await LinkTrail.trackInstall();

// iOS ATT / SKAdNetwork (no-ops on Android):
await LinkTrail.requestTrackingAuthorization();
LinkTrail.registerForSKAdAttribution();
LinkTrail.updateConversionValue(42, 'medium');

configure also accepts { logEnabled, logLevel, requestTimeoutMillis (default 15000), retryPolicy ({ maxAttempts: 3, baseDelayMillis: 500, maxDelayMillis: 8000 }), linkDomains, autoTrackInstall, autoHandleLinks }. Set autoHandleLinks: false to forward URLs yourself via LinkTrail.handleDeepLink(url) — it resolves null for URLs that aren't LinkTrail links.

onError receives { code, message }. Codes: missing_api_key, invalid_api_key, transport, server, decoding, empty_response, invalid_url, not_a_linktrail_url, not_configured, unknown. An invalid key surfaces here asynchronously (the backend rejects with 401) — configure itself only rejects on a blank key.

API surface

APIPurpose
configure(apiKey, options?)Initialize; auto-tracks the install
onLink((link, source) => …)Deep link delivered (deferred + re-engagement)
onAttribution((attribution) => …)Install attribution result
onError((error) => …)Failures (invalid key, network…)
handleDeepLink(url)Manual forward (with autoHandleLinks: false)
trackEvent(name, { value, currency })Custom + revenue events
trackInstall(force?)Manual install track (with autoTrackInstall: false)
getLastAttribution() / getLastDeepLink()Cached last results
requestTrackingAuthorization()ATT prompt (iOS; no-op on Android)
registerForSKAdAttribution() / updateConversionValue(value, coarse)SKAdNetwork (iOS; no-op on Android)

Standard app deep-link config — the wrapper handles the rest. Declare your LinkTrail host as a Universal Link (iOS Associated Domains: applinks:yourapp.linktrail.io) and an App Links intent-filter (Android), plus any custom scheme. LinkTrail hosts the apple-app-site-association / assetlinks.json files for your link domains.

If you set linkDomains, list EVERY link host. With a non-empty list, re-engagement opens (app already installed) only route for listed hosts — an unlisted host opens the app but never navigates. Deferred install links skip this check, so the bug hides on fresh installs. Leave linkDomains empty (the default) to handle every parseable link.
Example app: KickFlip — a storefront in example/ that demonstrates deferred deep linking end to end. Run cd example && npm install && npm run ios (or npm run android).