Skip to content

React Native SDK

The React Native SDK (@tolinku/react-native-sdk) provides event tracking, deferred deep linking, referrals, and in-app messages for React Native 0.72+.

Terminal window
npm install @tolinku/react-native-sdk

You also need the peer dependency for dismiss/impression state persistence:

Terminal window
npm install @react-native-async-storage/async-storage

If using Expo:

Terminal window
npx expo install @react-native-async-storage/async-storage

Initialize the SDK early in your app:

import { Tolinku } from '@tolinku/react-native-sdk';
// In App.tsx or index.js
Tolinku.init({
apiKey: 'tolk_pub_your_key',
// baseUrl: 'https://your-app.tolinku.com', // optional
// debug: true, // optional
// timeout: 30000 // optional, ms
});

Tolinku.configure() is the same call under the name the Android, iOS and Flutter SDKs use. Both work; init() is what this package shipped and is not deprecated.

Tolinku.setUserId('user_123');
// Get current user ID
const userId = Tolinku.getUserId();
// Clear on logout
Tolinku.setUserId(null);
// Simple event
await Tolinku.track('custom.screen_view');
// Event with properties
await Tolinku.track('custom.purchase', {
campaign: 'spring-sale',
user_id: 'user_123'
});
// Force flush
await Tolinku.flush();

Events are batched (10 events or 5-second timer) and auto-flushed when the app enters the background via React Native’s AppState listener.

Track purchases, cart activity, and product events via Tolinku.ecommerce:

Tolinku.setUserId('user_123');
// Track a purchase
await Tolinku.ecommerce.purchase({
transaction_id: 'order_456',
revenue: 49.99,
currency: 'USD',
items: [
{ item_id: 'sku_1', item_name: 'T-Shirt', price: 24.99, quantity: 2 }
]
});
// Track product views and cart events
await Tolinku.ecommerce.viewItem({ items: [{ item_id: 'sku_1' }] });
await Tolinku.ecommerce.addToCart({ items: [{ item_id: 'sku_1' }] });
await Tolinku.ecommerce.beginCheckout({});
// Search and ratings
await Tolinku.ecommerce.search({ search_term: 'shoes' });
await Tolinku.ecommerce.rate({ item_id: 'sku_1', rating: 4.5 });
// Force flush ecommerce events
await Tolinku.ecommerce.flush();

Ecommerce events are batched (10 events or 5-second timer) and auto-flushed when the app enters the background. The SDK manages cart IDs automatically via AsyncStorage, clearing them after purchase.

A link arrives as the URL that was tapped, exactly as it was written. That is enough while the URL is readable, but every route also has a short link, and a short link is the same route written as a code:

https://links.example.com/s7k2p9q/4821

Nothing in that URL says which route it is, and nothing on the device can work it out. Short links are what the dashboard’s copy button gives you and what a QR code carries, so your app will receive them whether or not you chose to share them.

links.resolve asks Tolinku and answers with the route, the token and the canonical path. A readable URL resolves to itself, so resolve every incoming link rather than trying to spot the short ones. It returns nothing rather than throwing when it cannot reach us, so your own handling stays the fallback.

import { Linking } from 'react-native';
import { Tolinku } from '@tolinku/react-native-sdk';
async function handle(url: string) {
const link = await Tolinku.links.resolve(url);
const path = link?.deep_link_path ?? new URL(url).pathname;
// path -> "/merchant/abc123", and link.token -> "abc123"
navigate(path);
}
Linking.addEventListener('url', ({ url }) => handle(url));
Linking.getInitialURL().then(url => url && handle(url));

link.token saves you working out which segment the token is, which the URL alone does not tell you when a route’s prefix places its token mid-path.

new URL(url).pathname is enough when you already know the shape you are getting and would rather not wait on the network. It can only tell you what the URL says, so it cannot expand a short code.

Recover the link a user tapped before they had your app, and route them to it on first launch.

const link = await Tolinku.deferred.claimDeferredLink({
appspaceId: '64f0a1b2c3d4e5f60718',
});
if (link) {
// Route to link.deep_link_path
}

Call it once, on the first launch after install. One call covers both platforms: on Android the SDK reads the Play Install Referrer itself, and everywhere else it falls back to device signals.

  1. Play Install Referrer, on Android only. A Tolinku link sends the visitor to the store with a token attached and Play hands it back on first launch, naming the exact click rather than inferring it.
  2. Device signals, if there was no referrer, and always on iOS, which has no equivalent. Timezone, language, screen size and pixel ratio are matched against what the landing page recorded.

The native module for the referrer ships with this package, so there is nothing extra to install. Autolinking is per platform, so an iOS build never compiles it. In Expo Go, where no custom native module is present, the call quietly falls back to signals.

A claim is consumed the first time it succeeds. claimDeferredLink remembers that it asked, so calling it again costs nothing.

Only a real answer is remembered. “Nothing waiting for this device” counts. A dropped request does not, so one bad connection does not spend the install’s only chance at attribution.

Both are still available and unchanged. They ask every time they are called and do no remembering, so use them only if you are doing that bookkeeping yourself.

const byToken = await Tolinku.deferred.claimByToken(token, appspaceId);
const bySignals = await Tolinku.deferred.claimBySignals({ appspaceId });

A link that opens your app directly never reaches Tolinku, so the tap is not counted. Those taps are the ones from people who already have your app, so leaving them out makes a campaign aimed at existing customers look like it got no traffic.

trackLinkOpen reports one. Call it wherever your app receives an incoming link.

A link arrives in two places and both need it. One that launches your app cold arrives somewhere different from one tapped while the app is already running, and instrumenting only the second misses the more common case while appearing to work.

// Launched by a link, app was not running.
const initial = await Linking.getInitialURL();
if (initial) Tolinku.trackLinkOpen(initial);
// Tapped while the app was already open.
Linking.addEventListener('url', ({ url }) => {
Tolinku.trackLinkOpen(url);
// your own routing
});

Wiring both is safe: some link plugins hand the launching link to the listener as well, and the same link inside a few seconds is reported once rather than counted twice.

Only http and https links are reported. A custom scheme means Tolinku’s own hand-off page opened your app, and that tap was counted when the page was served. The call never throws and never blocks.

Whether these are recorded is an Appspace setting, and it decides the bill. See Attributing app opens.

const { referrals } = Tolinku;
// Create a referral code
const result = await referrals.create({
userId: 'user_123',
userName: 'Jane Doe'
});
// Look up a referral
const info = await referrals.get('ABC123');
// Link a referred user (status stays pending until reward milestone is reached)
await referrals.complete({
code: 'ABC123',
referredUserId: 'user_456'
});
// Update milestone (completes the referral if it matches the reward milestone)
await referrals.milestone({ code: 'ABC123', milestone: 'first_purchase' });
// Claim reward (after granting it in your system)
await referrals.claimReward('ABC123');
// Get leaderboard
const { leaderboard } = await referrals.leaderboard(10);

Use the TolinkuMessages component to display in-app messages:

import { TolinkuMessages } from '@tolinku/react-native-sdk';
function App() {
return (
<View style={{ flex: 1 }}>
<MainContent />
<TolinkuMessages
trigger="on_open"
onDismiss={(messageId) => {
console.log('Dismissed:', messageId);
}}
onButtonPress={(action, messageId) => {
// Handle CTA action URL
}}
/>
</View>
);
}

The component fetches messages and displays the highest-priority undismissed message as a React Native modal.

PropTypeDescription
triggerstringFilter messages by trigger type
triggerValuestringFilter by trigger value
onDismissfunctionCalled when the user dismisses a message
onButtonPressfunctionCalled when the user taps a CTA button

Clean up when reconfiguring or unmounting:

await Tolinku.destroy();