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.
Before this works, set up three things in the dashboard. Skip one and links fail silently — they open the browser instead of your app. Full flow: Quickstart.
- 1Register both app targets — Apps → Register app. iOS needs your Apple Team ID and bundle ID; Android needs its package name and SHA-256 fingerprints.
- 2Add a link domain — Domains. Or use your free
*.linktrail.iosubdomain. - 3Create an API key — Settings → API Keys. That is the
lt_live_…value below.
Install
npm install linktrail-react-nativeThe 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.
{
"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:
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.
Deferred attribution & the paste button (iOS)
On iOS, deferred attribution recovers a click token the tapped link leaves on the clipboard. clickTokenSource, passed to configure, picks how it is read. Android uses the Play Install Referrer instead, so this setting is ignored there and the button renders nothing.
| clickTokenSource | Behaviour | Trade-off |
|---|---|---|
| 'pasteButton' (default) | Read only when the user taps <LinkTrailPasteButton/>. | No system alert, but it needs UI and autoTrackInstall: false. |
| 'automatic' | The SDK reads the clipboard itself at install. | No UI, but iOS shows the “Allow Paste” alert on first launch. |
| 'none' | The clipboard is never touched. | No alert and no UI; deferred matching falls back to probabilistic only. |
With the default 'pasteButton' you must render the button and set autoTrackInstall: false — the install fires when the token is pasted. Skip either and deferred installs on iOS stay unattributed.
import { LinkTrail, LinkTrailPasteButton } from 'linktrail-react-native';
await LinkTrail.configure('lt_live_...', {
clickTokenSource: 'pasteButton',
autoTrackInstall: false, // required: the install fires on tap
});
// On your first-launch screen:
<LinkTrailPasteButton
style={{ height: 46, alignSelf: 'stretch' }}
cornerStyle="fixed"
fillColor="#111"
foregroundColor="#FFFFFF"
onTokenPasted={(token) => {
// The SDK already fired the install; onLink routes the destination.
// Use this only for UI side effects.
}}
/>API surface
| API | Purpose |
|---|---|
| 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) |
Deep-link setup
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).