@smler/deferred-link
v1.0.0
Published
Lightweight React Native plugin enabling Android Install Referrer & iOS clipboard deferred deep linking to land users on exact screens.
Maintainers
Readme
React Native Deferred Deep Link
A powerful yet lightweight React Native plugin for deferred deep linking built for real production apps. It helps you extract referral information and deep link parameters on both Android and iOS without heavy attribution SDKs. Full TypeScript support included.
This is the React Native equivalent of the Flutter
smler_deferred_linkpackage, with 1:1 feature parity.
📌 What Is Deferred Deep Linking?
Deferred deep linking allows your user to install your app after clicking a link, and still land on the correct screen or carry referral metadata after install.
📘 How It Works
If the user has not installed the app and they click a deep link, it will first open in the phone's default browser. From the browser, the system automatically detects the platform and redirects to the respective store:
Android → Google Play Store
iOS → Apple App Store
After installation and first app launch, the app reads the deferred deep-link parameters and navigates to the exact intended screen.
Platform Behavior
Android
Uses the Google Play Install Referrer API (officially supported by Google). Reads details from:
https://play.google.com/store/apps/details?id=<package>&referrer=<encoded_params>From the referrer parameter, we decode and route the user to the correct screen.
iOS
Uses a clipboard deep link fallback that works even under iCloud+ Private Relay:
✔ The deep link is copied to the clipboard
✔ When the app is opened the first time, we read the clipboard
✔ If the link matches your allowed domains, we extract parameters and navigate
Installation
npm install react-native-smler-deferred-link
# or
yarn add react-native-smler-deferred-linkiOS
cd ios && pod installAndroid
No additional setup needed. The Google Play Install Referrer library is bundled automatically.
Peer Dependencies (optional)
For probabilistic matching (device fingerprint fallback), install:
npm install react-native-device-infoFor iOS clipboard access (required for iOS deferred deep linking):
React Native's built-in Clipboard is used by default. For newer React Native versions where it has been extracted, install:
npm install @react-native-clipboard/clipboardQuick Start
1. Android — Install Referrer
import { SmlerDeferredLink } from 'react-native-smler-deferred-link';
try {
const info = await SmlerDeferredLink.getInstallReferrerAndroid();
console.log('Raw referrer:', info.installReferrer);
// Parse as query parameters
const params = SmlerDeferredLink.parseReferrerParams(info);
console.log('utm_source:', params.utm_source);
console.log('utm_campaign:', params.utm_campaign);
// Get a single param
const uid = SmlerDeferredLink.getReferrerParam(info, 'uid');
// Extract shortCode and dltHeader
const { shortCode, dltHeader } =
SmlerDeferredLink.extractShortCodeAndDltHeader(info);
// Track click (automatic — reads clickId from referrer)
const tracking = await SmlerDeferredLink.trackClick(info);
if (tracking) console.log('Tracking response:', tracking);
} catch (e) {
console.error(e);
}ReferrerInfo fields:
| Field | Type | Description |
|---|---|---|
| installReferrer | string \| null | Raw referrer string from Google Play |
| referrerClickTimestampSeconds | number | Client-side click timestamp (seconds) |
| installBeginTimestampSeconds | number | Client-side install-begin timestamp (seconds) |
| referrerClickTimestampServerSeconds | number | Server-side click timestamp (seconds) |
| installBeginTimestampServerSeconds | number | Server-side install-begin timestamp (seconds) |
| installVersion | string \| null | App version at first install |
| googlePlayInstantParam | boolean | Whether instant experience launched in last 7 days |
2. iOS — Clipboard Deep Link
import { SmlerDeferredLink } from 'react-native-smler-deferred-link';
try {
const result = await SmlerDeferredLink.getInstallReferrerIos({
deepLinks: [
'https://example.com/profile',
'http://example.com/profile',
'example.com/profile',
'example.com', // base domain only
'*.example.com/profile/*', // wildcard subdomain + path
],
});
if (result) {
console.log('Deep link:', result.fullDeepLink);
console.log('Params:', result.queryParameters);
console.log('referrer:', result.queryParameters.referrer);
console.log('Short code:', result.pathParams.shortCode);
} else {
console.log('No matching deep link in clipboard');
}
} catch (e) {
console.error(e);
}IosClipboardDeepLinkResult fields:
| Field | Type | Description |
|---|---|---|
| fullDeepLink | string | Full clipboard text matched |
| fullReferralDeepLinkPath | string | Alias for fullDeepLink |
| queryParameters | Record<string, string> | Parsed ?key=value pairs |
| pathParams | PathParams | Extracted shortCode and dltHeader |
Pattern matching rules:
- Supports
http://,https://, and schemeless patterns - Strips
www.for comparison - Matches any subdomain:
sub.example.commatches patternexample.com - Wildcard host:
*.example.com - Wildcard path:
example.com/*,example.com/profile/* - Global wildcard:
*matches any parseable URL
3. Runtime Deep Links: resolveDeepLink()
When a user opens a deep link while the app is already installed, call this to resolve the short link and retrieve its full metadata.
import { Linking } from 'react-native';
import { SmlerDeferredLink } from 'react-native-smler-deferred-link';
Linking.addEventListener('url', async (event) => {
try {
const data = await SmlerDeferredLink.resolveDeepLink(event.url);
console.log('shortCode:', data.shortCode);
console.log('domain:', data.domain);
console.log('originalUrl:', data.originalUrl);
} catch (e) {
console.error('Error resolving link:', e);
}
});Pass triggerWebhook: true to instruct the Smler backend to fire the configured webhook
automatically in the same request — no second API call needed:
const data = await SmlerDeferredLink.resolveDeepLink(url, {
triggerWebhook: true,
});Parameters:
url(string): The full deep link URL that was openedoptions.triggerWebhook(boolean, optional): Whentrue, triggers the webhook server-side
Returns: ResolvedDeepLinkData with:
shortCode(string): The short URL codedomain(string): The domain the link belongs todltHeader(string?): Optional campaign/category headeroriginalUrl(string?): The original destination URL
4. Webhook Notification: triggerWebhook()
Call this after resolveDeepLink() to notify the Smler backend that the link was opened.
Tip: You can skip this separate call by passing
triggerWebhook: truedirectly toresolveDeepLink()(see above).
import { HelperReferrer } from 'react-native-smler-deferred-link';
const data = await SmlerDeferredLink.resolveDeepLink(deepLinkUrl);
if (data.shortCode && data.domain) {
await HelperReferrer.triggerWebhook({
shortCode: data.shortCode,
domain: data.domain,
dltHeader: data.dltHeader,
});
}Parameters:
shortCode(required): Short code from the resolved linkdomain(required): Domain from the resolved linkdltHeader(optional): Campaign header, if present
Returns: WebhookResponse — { success: true } on success, or an error object.
📊 Probabilistic Matching (Advanced Attribution)
Probabilistic matching enables accurate install attribution by analyzing device fingerprints when traditional methods fail. Particularly useful for iOS when clipboard matching fails.
When to Use
✅ iOS Fallback: When getInstallReferrerIos() returns null
✅ Cross-Platform Validation: Confirm attribution on both platforms
✅ Enhanced Attribution: Get additional click metadata
Usage
import { HelperReferrer } from 'react-native-smler-deferred-link';
const result = await HelperReferrer.getProbabilisticMatch({
domain: 'example.com',
clickId: 'optional-click-id', // optional
});
if (result.matched) {
console.log('Score:', result.score);
console.log('Short code:', result.pathParams?.shortCode);
console.log('Domain:', result.pathParams?.domain);
}Returns: ProbabilisticMatchResult with:
matched(boolean): Whether a match was foundscore(number | null): Confidence score (0.0 – 1.0)matchedAttributes: Attributes that matchedclickDetails: Details of the matched clickshortUrl: Complete short URL object with metadatafingerprint: Device fingerprint datapathParams:{ shortCode, dltHeader, domain }if matched
Note: Requires
react-native-device-infoto be installed for automatic device/OS detection. Falls back to 'Unknown' if not available.
Recommended First-Install Flow
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Platform } from 'react-native';
import {
SmlerDeferredLink,
HelperReferrer,
} from 'react-native-smler-deferred-link';
async function runDeferredAttribution() {
const isFirstInstall = await AsyncStorage.getItem('smler_first_install');
if (isFirstInstall !== null) return; // Already ran
if (Platform.OS === 'android') {
const info = await SmlerDeferredLink.getInstallReferrerAndroid();
const params = SmlerDeferredLink.parseReferrerParams(info);
// Route based on params.utm_campaign, etc.
} else if (Platform.OS === 'ios') {
const result = await SmlerDeferredLink.getInstallReferrerIos({
deepLinks: ['https://yourdomain.com', 'yourdomain.com'],
});
if (result) {
// Clipboard match found — resolve it
const data = await SmlerDeferredLink.resolveDeepLink(
result.fullDeepLink,
{ triggerWebhook: true }
);
// Route to screen based on data
} else {
// Fallback to probabilistic matching
const match = await HelperReferrer.getProbabilisticMatch({
domain: 'yourdomain.com',
});
if (match.matched && (match.score ?? 0) > 0.65) {
// High confidence match — route based on match.pathParams
}
}
}
await AsyncStorage.setItem('smler_first_install', 'done');
}Backend Support (Important)
You must handle one small backend/website step: When a user clicks a deep link, your web page should redirect them to the appropriate store.
Android:
https://play.google.com/store/apps/details?id=<package>&referrer=<URL-encoded referrer>iOS:
Copy the deep link to the clipboard, then redirect to the App Store:
// On your landing page
navigator.clipboard.writeText(window.location.href);
window.location.href = 'https://apps.apple.com/app/id<YOUR_APP_ID>';API Reference
SmlerDeferredLink
| Method | Platform | Description |
|---|---|---|
| getInstallReferrerAndroid() | Android | Read Google Play Install Referrer |
| parseReferrerParams(info) | Android | Parse referrer string as query params |
| getReferrerParam(info, key) | Android | Get a single referrer param |
| extractShortCodeAndDltHeader(info) | Android | Extract shortCode/dltHeader from referrer URL |
| trackClick(info) | Android | Track click via referrer's clickId |
| getInstallReferrerIos(options) | iOS | Read clipboard and match against deep link patterns |
| resolveDeepLink(url, options?) | Both | Resolve a deep link URL via Smler API |
HelperReferrer
| Method | Description |
|---|---|
| matchesDeepLinkPattern(clipboard, pattern) | Check if a URL matches a deep link pattern |
| fetchTrackingData(clickId, pathParams, domain?) | Fetch tracking data from Smler API |
| getProbabilisticMatch(options) | Probabilistic install attribution |
| resolveDeepLinkData(url, options?) | Resolve deep link URL |
| triggerWebhook(options) | Trigger webhook notification |
| extractShortCodeAndDltHeader(url) | Extract shortCode/dltHeader from URL |
| parseReferrerAsQueryParams(referrer) | Parse referrer string as params |
TypeScript Types
All types are exported from the package:
import type {
ReferrerInfo,
IosClipboardDeepLinkResult,
ProbabilisticMatchResult,
TrackingResponse,
WebhookResponse,
ResolvedDeepLinkData,
PathParams,
} from 'react-native-smler-deferred-link';License
MIT © Bokimo Technologies LLP
