Skip to main content

React Native SDK

Integrate deep linking and mobile attribution into your React Native app with the LinkForty SDK.

Expo app?

If you're building with Expo, use the Expo SDK instead. It provides the same features with a pure-JS implementation and no native code required.

Faster setup with AI

Already using Claude Code, Cursor, or Claude Desktop? The LinkForty MCP server can scaffold your SDK integration automatically. Just ask: "Help me add the LinkForty SDK to my React Native app."

Features

  • Deferred deep linking -- route new users to specific content after install
  • Direct deep linking -- handle links when the app is already installed
  • Install attribution -- match installs to the link click that drove them
  • Event tracking -- log in-app events tied to attribution data
  • Programmatic link creation -- create short links from your app
  • Server-side URL resolution -- resolve Universal Links / App Links for enriched data
  • TypeScript support -- full type definitions included
  • Cross-platform -- iOS and Android with a single codebase

Requirements

  • React Native >= 0.64.0
  • React >= 17.0.0
  • Node.js >= 20.0.0

Installation

npm install @linkforty/mobile-sdk-react-native

The SDK bundles @react-native-async-storage/async-storage and react-native-device-info as direct dependencies -- no additional peer dependency installs are needed.

iOS

After installing, run:

cd ios && pod install && cd ..

Android

No additional setup required. Auto-linking handles native module registration.

Platform Configuration

  1. In Xcode, add the Associated Domains capability to your target
  2. Add your domain: applinks:go.yourdomain.com
  3. Ensure your LinkForty server hosts the Apple App Site Association file at /.well-known/apple-app-site-association (LinkForty serves this automatically)

Add an intent filter to your AndroidManifest.xml:

<activity
android:name=".MainActivity"
android:launchMode="singleTask">

<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" android:host="go.yourdomain.com" />
</intent-filter>
</activity>

Ensure your LinkForty server hosts the Digital Asset Links file at /.well-known/assetlinks.json (LinkForty serves this automatically).


Quick Start

Initialize the SDK

Call init() once at app startup, before registering any callbacks:

import React, { useEffect } from 'react';
import LinkForty, { DeepLinkData } from '@linkforty/mobile-sdk-react-native';

function App() {
useEffect(() => {
LinkForty.init({
baseUrl: 'https://go.yourdomain.com',
apiKey: 'your-api-key', // optional -- required for link creation
appToken: 'at_your_app_token', // recommended for Cloud -- enables organic-install attribution
debug: __DEV__,
});

// Handle deferred deep links (new installs)
LinkForty.onDeferredDeepLink((data: DeepLinkData | null) => {
if (data) {
console.log('Attributed install:', data);
navigateToContent(data);
}
});

// Handle direct deep links (app already installed)
LinkForty.onDeepLink((url: string, data: DeepLinkData | null) => {
console.log('Deep link opened:', url);
if (data) {
navigateToContent(data);
}
});
}, []);

const navigateToContent = (data: DeepLinkData) => {
const productId = data.customParameters?.productId;
if (productId) {
// Navigate to product screen
}
};

return (/* your app */);
}

Track Events

import LinkForty from '@linkforty/mobile-sdk-react-native';

// Track a custom event
LinkForty.trackEvent('purchase', {
value: 29.99,
currency: 'USD',
productId: '123',
});

// Track a signup
LinkForty.trackEvent('signup', { method: 'email' });

// Track revenue (standardized format for dashboard aggregation)
LinkForty.trackRevenue(29.99, 'USD', { productId: '123', orderId: 'order-456' });
import LinkForty from '@linkforty/mobile-sdk-react-native';

const result = await LinkForty.createLink({
deepLinkParameters: { route: 'PRODUCT', id: '123' },
title: 'Check out this product',
utmParameters: { source: 'app', medium: 'share' },
});

console.log(result.url); // https://go.yourdomain.com/abc123
console.log(result.shortCode); // abc123
console.log(result.linkId); // uuid
warning

Link creation requires an API key. Pass apiKey in your init() configuration. The simplified endpoint (/api/sdk/v1/links) is only available on LinkForty Cloud, not self-hosted Core.


API Reference

init(config)

Initialize the SDK. Must be called before any other method. Idempotent -- calling it twice logs a warning and returns.

await LinkForty.init(config: LinkFortyConfig): Promise<void>
ParameterTypeRequiredDescription
baseUrlstringYesYour LinkForty server URL
apiKeystringNoAPI key for link creation and Cloud features
appTokenstringNoPublic workspace token. Recommended for Cloud — required for organic installs (App Store discovery, social mentions, etc.) to be attributed to your workspace. Find it in the dashboard under Workspace Settings → App Token. Safe to ship in your app bundle.
debugbooleanNoEnable verbose logging (default: false)
attributionWindownumberNoAttribution window in days (default: 7)
autoTrackNavigationboolean | objectNoAuto-emit screen_view events from React Navigation. Requires navigationRef. See Automatic Screen Tracking. Default: off.
navigationRefrefNoYour React Navigation container ref. Required when autoTrackNavigation is enabled.

onDeferredDeepLink(callback)

Register a callback for deferred deep links. If attribution data is already available (e.g., callback registered after init completes), the callback fires immediately.

LinkForty.onDeferredDeepLink(callback: (data: DeepLinkData | null) => void): void

The callback receives null for organic installs (no attribution match).

onDeepLink(callback)

Register a callback for direct deep links. When a LinkForty URL opens the app, the SDK resolves it server-side for enriched data, then invokes the callback.

LinkForty.onDeepLink(callback: (url: string, data: DeepLinkData | null) => void): void

trackEvent(name, properties?)

Track an in-app event. Requires a successful install report (install ID must be available).

await LinkForty.trackEvent(name: string, properties?: Record<string, any>): Promise<void>

trackRevenue(amount, currency, properties?)

Track a revenue event using the standardized format. The amount must be non-negative. All four LinkForty SDKs use the same convention so the Events dashboard can aggregate revenue.

await LinkForty.trackRevenue(amount: number, currency: string, properties?: Record<string, any>): Promise<void>
ParameterTypeRequiredDescription
amountnumberYesNon-negative revenue amount
currencystringYesISO 4217 currency code (e.g., USD)
propertiesRecord<string, any>NoAdditional event properties

Internally calls trackEvent('revenue', { revenue: amount, currency, ...properties }).

createLink(options)

Create a short link programmatically. Requires an API key.

await LinkForty.createLink(options: CreateLinkOptions): Promise<CreateLinkResult>
OptionTypeRequiredDescription
templateIdstringNoTemplate UUID (uses organization's default template if omitted)
templateSlugstringNoTemplate slug for URL construction
deepLinkParametersRecord<string, string>NoIn-app routing parameters
titlestringNoLink title
descriptionstringNoLink description
customCodestringNoCustom short code
utmParametersUTMParametersNoCampaign tracking parameters
externalUserIdstringNoUser ID for deduplication and share attribution

Returns: { url: string, shortCode: string, linkId: string, deduplicated?: boolean }

setExternalUserId(id)

Set the external user ID for share attribution. This ID is automatically attached to all createLink() calls unless overridden per-call. Pass null to clear.

LinkForty.setExternalUserId(id: string | null): void

getExternalUserId()

Returns the current external user ID, or null if not set.

LinkForty.getExternalUserId(): string | null

getInstallId()

Get the server-assigned install UUID. Returns null if no install has been reported.

await LinkForty.getInstallId(): Promise<string | null>

getInstallData()

Retrieve cached attribution data from local storage. Returns null for organic installs.

await LinkForty.getInstallData(): Promise<DeepLinkData | null>

clearData()

Wipe all locally stored SDK data (install ID, attribution data, first-launch flag). The next app launch will be treated as a fresh install.

await LinkForty.clearData(): Promise<void>

Types

import type {
LinkFortyConfig,
DeepLinkData,
InstallAttributionResponse,
CreateLinkOptions,
CreateLinkResult,
DeferredDeepLinkCallback,
DeepLinkCallback,
} from '@linkforty/mobile-sdk-react-native';

DeepLinkData

FieldTypeDescription
shortCodestringThe link's short code
iosUrlstring?iOS destination URL
androidUrlstring?Android destination URL
webUrlstring?Web fallback URL
utmParametersUTMParameters?UTM tracking parameters
customParametersRecord<string, string>?Custom query parameters
deepLinkPathstring?In-app routing path (e.g., /product/123)
appSchemestring?App URI scheme (e.g., myapp)
clickedAtstring?When the link was clicked (ISO 8601)
linkIdstring?Link UUID

UTMParameters

FieldType
sourcestring?
mediumstring?
campaignstring?
termstring?
contentstring?

Automatic Screen Tracking

Enable autoTrackNavigation to emit a screen_view event on every React Navigation transition — no manual trackEvent calls per screen. Screen views flow through the normal event pipeline and carry the active deep-link attribution context, so they power App Analytics screen flow and the SDK health "events flowing" signal.

It requires a navigation container ref. Pass it as navigationRef:

import { createNavigationContainerRef, NavigationContainer } from '@react-navigation/native';
import LinkForty from '@linkforty/mobile-sdk-react-native';

export const navigationRef = createNavigationContainerRef();

LinkForty.init({
baseUrl: 'https://go.yourdomain.com',
appToken: 'at_your_app_token',
autoTrackNavigation: true, // screen names only — privacy-safe default
navigationRef,
});

// Attach the same ref to your container
<NavigationContainer ref={navigationRef}>
{/* ...screens... */}
</NavigationContainer>

Capturing route params

By default, autoTrackNavigation: true captures screen names only — never route params, since params can hold personal data. To capture specific, non-PII params, pass an options object with an explicit allow-list:

LinkForty.init({
baseUrl: 'https://go.yourdomain.com',
navigationRef,
autoTrackNavigation: {
captureParams: ['productId', 'category'], // only these keys are captured
debounceMs: 350, // collapse rapid transitions (default 350ms)
},
});
OptionTypeDefaultDescription
captureParamsstring[][] (none)Allow-list of route param keys whose primitive values may be captured. Never list keys that can hold personal data.
debounceMsnumber350Debounce window for rapid transitions, in milliseconds.

What gets sent

Each transition emits an event named screen_view with:

PropertyDescription
screenThe active route name
previousScreenThe route navigated from (when available)
paramsAllow-listed route params (only if captureParams is set)

Rapid transitions are debounced to the final screen and consecutive duplicates are dropped. Apps that don't use React Navigation are unaffected — if autoTrackNavigation is enabled without a navigationRef, screen tracking is simply disabled (a warning is logged in debug mode).

SDK version

Automatic screen tracking is available in the SDK versions that ship the navigation tracker. Check the SDK health page to confirm your installed version supports it.


Self-Hosted Configuration

If you're running LinkForty Core instead of Cloud, omit the apiKey:

await LinkForty.init({
baseUrl: 'https://links.yourcompany.com',
debug: false,
});

Link creation via createLink() without a templateId is not available on self-hosted Core -- you must provide a templateId and use the /api/links endpoint.


Troubleshooting

  1. Attribution window expired -- the user installed too long after clicking the link
  2. Fingerprint mismatch -- different network between click and install, VPN, or iOS privacy relay
  3. SDK not initialized -- ensure init() is called before onDeferredDeepLink()
  4. Enable debug mode -- set debug: true to see detailed logs

Events not appearing in analytics

  1. No install ID -- events require a successful install report. Check debug logs for install errors
  2. Network issues -- the React Native SDK does not queue events offline (events are fire-and-forget)

iOS build errors

cd ios && pod deintegrate && pod install && cd ..

Android build errors

Ensure minSdkVersion is at least 21 in android/build.gradle.


Parity Notes

The React Native SDK has some gaps compared to the iOS, Android, and Expo SDKs:

  • No offline event queue -- events are fire-and-forget; failed events are not retried
  • No reset() method -- only clearData() is available
  • No typed errors -- throws generic Error instead of typed error codes
  • No configuration validation -- HTTPS and attribution window bounds are not enforced

See the SDK Specification for the full feature parity matrix.


Next Steps

Resources