Official React Native SDK for LinkForty - Open-source deep linking and mobile attribution platform.
- Direct Deep Linking - Handle Universal Links (iOS) and App Links (Android) with automatic server-side resolution
- Deferred Deep Linking - Route new users to specific content after install
- Link Creation - Create short links programmatically from your app
- Probabilistic Attribution - Match installs to clicks via device fingerprinting
- Event Tracking - Track in-app events with attribution
- Privacy-Focused - No persistent device IDs required
- TypeScript Support - Full type definitions included
- Works with Core & Cloud - Compatible with self-hosted and Cloud instances
npm install @linkforty/mobile-sdk-react-native- React Native >= 0.64.0
- React >= 17.0.0
- Node.js >= 20.0.0
npm install @react-native-async-storage/async-storage react-native-device-info- Install CocoaPods dependencies:
cd ios && pod install-
Configure Universal Links in Xcode:
- Open your project in Xcode
- Select your app target > Signing & Capabilities
- Add "Associated Domains" capability
- Add domain:
applinks:go.yourdomain.com(replace with your LinkForty domain)
-
Host an Apple App Site Association (AASA) file at:
https://go.yourdomain.com/.well-known/apple-app-site-association{ "applinks": { "apps": [], "details": [ { "appID": "TEAM_ID.com.yourapp.bundle", "paths": ["*"] } ] } }
- Add an App Links intent filter in
android/app/src/main/AndroidManifest.xml:
<activity android:name=".MainActivity">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:host="go.yourdomain.com" />
</intent-filter>
</activity>-
Host a Digital Asset Links file at:
https://go.yourdomain.com/.well-known/assetlinks.json[{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "android_app", "package_name": "com.yourapp", "sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"] } }] -
Ensure
MainActivitypreserves the Intent for React Native. InMainActivity.kt:
override fun onNewIntent(intent: Intent) {
setIntent(intent) // Required for React Native's Linking.getInitialURL()
super.onNewIntent(intent)
}Note: If you use other SDKs that consume Intent data (e.g., CleverTap), make sure they receive a copy of the URI in
onCreaterather than consuming the original Intent, otherwiseLinking.getInitialURL()may returnnull.
import LinkForty from '@linkforty/mobile-sdk-react-native';
// 1. Initialize the SDK (call once at app startup)
await LinkForty.init({
baseUrl: 'https://go.yourdomain.com',
apiKey: 'your-api-key', // Required for createLink(), optional otherwise
debug: __DEV__,
});
// 2. Handle direct deep links (user taps a link while app is installed)
LinkForty.onDeepLink((url, data) => {
if (data?.customParameters) {
const { route, id } = data.customParameters;
// Navigate to the target screen
navigation.navigate(route, { id });
}
});
// 3. Handle deferred deep links (user installs app after tapping a link)
LinkForty.onDeferredDeepLink((data) => {
if (data?.customParameters) {
const { route, id } = data.customParameters;
navigation.navigate(route, { id });
} else {
console.log('Organic install — no link clicked');
}
});Initialize the SDK. Must be called before any other method.
await LinkForty.init({
baseUrl: 'https://go.yourdomain.com', // Required
apiKey: 'your-api-key', // Optional (required for createLink)
debug: true, // Optional (default: false)
attributionWindow: 7, // Optional, in days (default: 7)
});| Parameter | Type | Required | Description |
|---|---|---|---|
baseUrl |
string |
Yes | Base URL of your LinkForty instance |
apiKey |
string |
No | API key for authenticated endpoints (Cloud) |
debug |
boolean |
No | Enable debug logging |
attributionWindow |
number |
No | Attribution window in days (default: 7) |
Listen for direct deep links. Called when the app is opened via a Universal Link (iOS) or App Link (Android).
When the URL matches your LinkForty baseUrl, the SDK automatically resolves it via the server to retrieve the full link data including customParameters, UTM parameters, and metadata. If resolution fails, the SDK falls back to local URL parsing.
LinkForty.onDeepLink((url: string, data: DeepLinkData | null) => {
console.log('Link URL:', url);
if (data?.customParameters) {
const { route, id } = data.customParameters;
navigation.navigate(route, { id });
}
});| Callback Parameter | Type | Description |
|---|---|---|
url |
string |
The full URL that opened the app |
data |
DeepLinkData | null |
Parsed link data, or null if parsing failed |
Listen for deferred deep links. Called on first launch after install if the user clicked a LinkForty link before installing.
LinkForty.onDeferredDeepLink((data: DeepLinkData | null) => {
if (data) {
// Attributed install — user clicked a link before installing
console.log('Came from:', data.utmParameters?.source);
const { route, id } = data.customParameters || {};
if (route) navigation.navigate(route, { id });
} else {
// Organic install or attribution failed
console.log('Organic install');
}
});| Callback Parameter | Type | Description |
|---|---|---|
data |
DeepLinkData | null |
Attributed link data, or null for organic installs |
Create a short link programmatically. Requires apiKey to be set in init().
When templateId is omitted, the server auto-selects your organization's default template.
const result = await LinkForty.createLink({
deepLinkParameters: { route: 'VIDEO_VIEWER', id: 'e4338ed6-...' },
title: 'Check out this video',
utmParameters: { source: 'share', medium: 'app' },
});
console.log(result.url); // https://go.yourdomain.com/tmpl/abc123
console.log(result.shortCode); // abc123
console.log(result.linkId); // uuidOptions:
| Parameter | Type | Required | Description |
|---|---|---|---|
deepLinkParameters |
Record<string, string> |
No | Custom parameters embedded in the link (e.g., { route, id }) |
title |
string |
No | Link title for internal reference |
description |
string |
No | Link description |
customCode |
string |
No | Custom short code (auto-generated if omitted) |
utmParameters |
object |
No | UTM parameters (source, medium, campaign, term, content) |
templateId |
string |
No | Template UUID (auto-selected if omitted) |
templateSlug |
string |
No | Template slug (only needed with templateId) |
Returns: CreateLinkResult
| Field | Type | Description |
|---|---|---|
url |
string |
Full shareable URL |
shortCode |
string |
The generated short code |
linkId |
string |
Link UUID |
Track an in-app event for attribution analytics. Requires a successful install report (automatic on first launch).
await LinkForty.trackEvent('purchase', {
amount: 99.99,
currency: 'USD',
productId: 'premium_plan',
});| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Event name (e.g., 'purchase', 'signup') |
properties |
Record<string, any> |
No | Arbitrary event properties |
Retrieve cached install attribution data from a previous deferred deep link.
const data = await LinkForty.getInstallData();
if (data) {
console.log('Install attributed to:', data.utmParameters?.source);
}Returns: DeepLinkData | null
Get the unique install ID assigned by the LinkForty server on first launch.
const installId = await LinkForty.getInstallId();Returns: string | null
Clear all cached SDK data (install ID, attribution data, first-launch flag). The next app launch will behave as a fresh install.
await LinkForty.clearData();interface DeepLinkData {
shortCode: string;
customParameters?: Record<string, string>;
utmParameters?: {
source?: string;
medium?: string;
campaign?: string;
term?: string;
content?: string;
};
iosUrl?: string;
androidUrl?: string;
webUrl?: string;
deepLinkPath?: string;
appScheme?: string;
clickedAt?: string;
linkId?: string;
}interface LinkFortyConfig {
baseUrl: string;
apiKey?: string;
debug?: boolean;
attributionWindow?: number;
}interface CreateLinkOptions {
templateId?: string;
templateSlug?: string;
deepLinkParameters?: Record<string, string>;
title?: string;
description?: string;
customCode?: string;
utmParameters?: {
source?: string;
medium?: string;
campaign?: string;
term?: string;
content?: string;
};
}interface CreateLinkResult {
url: string;
shortCode: string;
linkId: string;
}When a user taps a LinkForty URL and the app is already installed:
- The OS intercepts the URL via App Links (Android) or Universal Links (iOS) and opens your app directly
- The SDK receives the URL via React Native's
LinkingAPI - The SDK calls your LinkForty server's resolve endpoint to retrieve the full link data (
customParameters, UTM params, etc.) - Your
onDeepLinkcallback fires with the resolved data - Your app navigates to the target screen
When a user taps a LinkForty URL and the app is not installed:
- The LinkForty server records a click with the user's device fingerprint
- The user is redirected to the App Store / Play Store
- After installing and opening the app, the SDK reports the install with the device's fingerprint
- The server matches the fingerprint to the original click (probabilistic attribution)
- Your
onDeferredDeepLinkcallback fires with the matched link data - Your app navigates to the content the user originally clicked on
- Verify AASA file is accessible at
https://yourdomain.com/.well-known/apple-app-site-association - Check that Team ID and Bundle ID are correct in the AASA file
- Confirm "Associated Domains" capability is added in Xcode with
applinks:yourdomain.com - Test on a real device (Universal Links don't work in the simulator)
- Verify assetlinks.json is accessible at
https://yourdomain.com/.well-known/assetlinks.json - Check package name and SHA256 fingerprint are correct
- Run
adb shell pm get-app-links com.yourappto check link verification status - Confirm
android:autoVerify="true"is set in your intent filter - Ensure
MainActivitycallssetIntent(intent)inonNewIntent(see Android Setup)
This usually means another SDK or library is consuming the Intent data before React Native reads it. In MainActivity.kt:
- Call
setIntent(intent)inonNewIntentso React Native sees the updated Intent on warm starts - If using CleverTap or similar SDKs, pass a copy of the URI to them in
onCreaterather than the original Intent data
- Confirm this is a first install (or call
clearData()first) - Enable
debug: trueand check logs for fingerprint data - Verify your LinkForty server received the install event
- Ensure the click and install happen within the attribution window (default: 7 days)
- Test on the same network for best fingerprint match accuracy
Ensure peer dependencies are installed:
npm install --save-dev @types/react @types/react-nativeContributions are welcome! See CONTRIBUTING.md for guidelines.
MIT License - see LICENSE for details.
- Issues: GitHub Issues
- Documentation: LinkForty Core
- LinkForty Cloud - Cloud platform with dashboard and API
- LinkForty Core - Self-hosted open-source backend