React Native SDK
Full reference for @onramp-sdk/react-native. Works with bare React Native and Expo (managed and bare workflow).
For a complete small app with a real onboarding flow, see the React Native + Expo example on GitHub.
Installation
npm install @onramp-sdk/react-native
# or
yarn add @onramp-sdk/react-native
Milestone tracking, sessions, and deep-link attribution use only JavaScript APIs (AsyncStorage via @react-native-async-storage/async-storage) - no rebuild needed for those. Android Play Install Referrer and iOS Apple Search Ads (see Attribution below) each add a small native module; run npx pod-install / rebuild once after upgrading to pick them up on whichever platform(s) you enable.
AsyncStorage peer dependency
If your project does not already have @react-native-async-storage/async-storage installed, add it:
npm install @react-native-async-storage/async-storage
For Expo managed workflow, see the Expo SDK page.
Setup
Call OnRamp.init() once when your app starts - before any OnRamp.step() calls. A good place is the root App component.
import { useEffect } from 'react'
import { OnRamp } from '@onramp-sdk/react-native'
export default function App() {
useEffect(() => {
OnRamp.init({ apiKey: 'onr_xxxxxxxxxxxx' })
}, [])
return (
<NavigationContainer>
<RootStack />
</NavigationContainer>
)
}
Your API key is on the Settings page of each app in the dashboard. It looks like onr_a3f8d2e1b9c4....
OnRamp.init() options
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey | string | Yes | - | Your API key from Settings |
appVersion | string | No | - | App version string, e.g. "2.4.1" - shows in the version breakdown |
sessionTimeoutMs | number | No | 1_800_000 | Idle time (ms) before a new session starts (default 30 min) |
host | string | No | https://ingest.getonramp.dev | Override ingestion endpoint (for self-hosting) |
captureInstallReferrer | boolean | No | false | Capture install attribution (deep links, Play Install Referrer on Android, Apple Search Ads on iOS) automatically - opt-in, see Attribution below |
Tracking steps
OnRamp.step(name, options?)
Records a funnel milestone. Safe to call anywhere - the SDK queues events if the network is unavailable and flushes when connectivity returns.
import { OnRamp } from '@onramp-sdk/react-native'
// Basic
OnRamp.step('account_created')
// With custom properties
OnRamp.step('subscription_started', {
properties: {
plan: 'pro',
billing_period: 'annual',
price_usd: 79.99,
},
})
Options
| Option | Type | Description |
|---|---|---|
properties | Record<string, string | number | boolean> | Custom key-value data attached to the event |
Property values must be primitives - strings, numbers, or booleans. Nested objects are not supported. Numeric values become queryable as custom metrics in the funnel chart.
Screen tracking
Navigation adapter (React Navigation)
Wrap your NavigationContainer with NavigationTracker to automatically record every screen change:
import { NavigationContainer } from '@react-navigation/native'
import { NavigationTracker } from '@onramp-sdk/react-native'
export default function App() {
useEffect(() => {
OnRamp.init({ apiKey: 'onr_xxxxxxxxxxxx' })
}, [])
return (
<NavigationTracker>
<NavigationContainer>
<RootStack />
</NavigationContainer>
</NavigationTracker>
)
}
Each screen change is recorded as a navigation event with the route name. These appear in session timelines and the journey map but do not count toward funnel step conversion.
Identifying users
OnRamp.identify(traits)
Associates the current anonymous user with known identity traits. Call once after sign-in so integrations (Stripe, RevenueCat) can match the user to external records.
import { OnRamp } from '@onramp-sdk/react-native'
// After the user signs in
OnRamp.identify({ email: user.email, userId: user.id })
identify() is entirely optional. All funnel and retention features work without it. Only call it if you have an integration connected and want to correlate OnRamp sessions with external revenue data.
Attribution
The SDK can capture which channel or campaign drove an install, so you aren't blind to what's working the way you would be with milestone data alone. This is off by default - set captureInstallReferrer: true to turn it on:
OnRamp.init({ apiKey: 'onr_xxxxxxxxxxxx', captureInstallReferrer: true })
It defaults to false rather than true for two reasons: turning it on starts collecting a new category of data (campaign/referrer info) that you should consciously opt into rather than get for free on an SDK upgrade, and on Android it reads an OS-level API (Play Install Referrer) that some apps already read via an MMP (Adjust, AppsFlyer, Branch) - enabling it blind could put two readers in competition for the same one-time value. OnRamp.setAttribution() (below) works regardless of this flag, so it's always safe to call if you get attribution data from elsewhere - whether or not you've turned on OnRamp's own capture on either platform.
Deep links
On app launch, OnRamp reads utm_source / utm_medium / utm_campaign / utm_term / utm_content from the Universal Link or App Link that opened the app (falling back to known ad click IDs - gclid, fbclid, etc - the same mapping the web SDK uses). This is attached once, to the very first tracked event for the install, and never re-attached on later app opens.
myapp://open?utm_source=newsletter&utm_medium=email&utm_campaign=spring_launch
Setting up the Universal Link (iOS associatedDomains) or App Link (Android intentFilters) itself is a separate, still-required app configuration step - the SDK only reads the URL once your app is already configured to receive it.
Android Play Install Referrer
On Android, the SDK also reads the Play Install Referrer string attached to the install - this catches Play Console campaign links and Google Ads app-campaign installs even when there was no deep link involved (e.g. a plain Play Store listing visit). It requires a native rebuild (this SDK version ships a small native Android module - no manual linking needed, React Native autolinking picks it up).
If both a deep link and the Play Install Referrer resolve on the same launch, the Play Install Referrer wins - it's the more authoritative install-time signal. There's no Android/iOS split to worry about here: this only runs on Android, and iOS deep-link capture is unaffected.
Single-consumer API
Play Install Referrer can only be read reliably once per install, by one consumer. If your app already runs an MMP (Adjust, AppsFlyer, Branch) that also reads it, leave captureInstallReferrer at its default false and use OnRamp.setAttribution() (below) instead of also enabling OnRamp's own read.
iOS Apple Search Ads
On iOS, the SDK also captures the on-device Apple Search Ads attribution token (via the AdServices framework, available automatically on iOS 14.3+) and sends it to OnRamp, which resolves it server-side into a campaign/keyword - the token itself is opaque and can't be resolved on-device. This requires a native rebuild the same way Android's Play Install Referrer does (this SDK version ships a small native iOS module via its own podspec - pod install picks it up, no manual linking needed).
Neither this nor deep link capture requires the device advertising identifier (IDFA) or an App Tracking Transparency prompt.
Resolution is asynchronous
Unlike deep links and Play Install Referrer, this data can take minutes to hours to appear in the dashboard - it depends on a server-to-server round trip to Apple's attribution API, not something available at track time. It also only reveals the paid keyword/campaign behind a Search Ads impression; there's no API (Apple or Google) for organic App Store/Play Store search-term data.
OnRamp.setAttribution()
If you already use an MMP (Adjust, AppsFlyer, Branch, etc.) for install attribution, leave captureInstallReferrer at its default false and call this from your MMP's attribution-resolved callback instead:
OnRamp.init({ apiKey: 'onr_xxxxxxxxxxxx' }) // captureInstallReferrer left at its default (false)
// Inside your MMP's attribution callback:
OnRamp.setAttribution({
provider: 'appsflyer',
source: 'facebook',
medium: 'paid_social',
campaign: 'summer_promo',
})
This works independently of captureInstallReferrer - it's always safe to call, whether or not you've turned on OnRamp's own capture. Leaving that flag off avoids two SDKs competing over OS-level, single-consumer attribution APIs (see the Android SDK page for why that matters for Play Install Referrer specifically). setAttribution() no-ops if attribution has already been attached to this install's first tracked event.
MMP integrations
OnRamp has first-class callback adapters for AppsFlyer, Adjust, Branch, Singular, Kochava, Airbridge, and Tenjin. Install and initialize only your chosen MMP using its own documentation; OnRamp intentionally does not bundle, initialize, or transmit data to any MMP.
Pass the raw resolved-attribution callback to OnRamp. It maps the provider's network, campaign, ad set, and creative fields to OnRamp's source/campaign breakdowns:
// AppsFlyer onConversionDataSuccess(data)
OnRamp.setMMPAttribution('appsflyer', data)
// Adjust attribution callback
OnRamp.setMMPAttribution('adjust', attribution)
// Branch subscribe() referring params
OnRamp.setMMPAttribution('branch', params)
setMMPAttribution() returns false for incomplete/organic payloads and if the first event has already been recorded, so it is safe to call directly inside each provider callback. For a custom provider, use the explicit API:
OnRamp.setAttribution({
provider: 'my_mmp',
source: result.network,
medium: result.channel,
campaign: result.campaign,
term: result.adGroup,
content: result.creative,
})
| MMP | Callback fields mapped by OnRamp |
|---|---|
| AppsFlyer | media_source, af_channel, campaign, af_adset, af_ad |
| Adjust | network, campaign, adgroup, creative |
| Branch | ~feature, ~channel, ~campaign, ~tags, ~creative_name |
| Singular | source, campaign_name, adgroup_name, creative_name |
| Kochava | network_name, campaign_name, ad_group_name, creative_name |
| Airbridge | channel, campaign, ad_group, ad_creative |
| Tenjin | ad_network, campaign_name, adgroup_name, creative_name |
MMP attribution is authoritative when it arrives before the first tracked event. The dashboard groups it under MMPs, as well as by source, campaign, and attribution channel. Leave captureInstallReferrer off when an MMP owns install attribution so two SDKs do not compete for the Play Install Referrer.
Neither Apple nor Google exposes organic App Store/Play Store search-term data. Search-term attribution (via Apple Search Ads) only covers paid keywords behind a Search Ads impression.
Not covered: ad-network postback attribution
SKAdNetwork (Facebook/TikTok/etc install attribution) and deferred deep linking (matching a pre-install ad click to an install after a detour through the App/Play Store) aren't built into OnRamp - that's full MMP territory (Adjust/AppsFlyer/Branch). If you already have one of those for this, OnRamp.setAttribution() above is the way to bring its resolved data into your OnRamp funnels.
Session management
OnRamp.newSession()
Force-starts a new session - useful after logout so the next user gets a clean session.
async function handleLogout() {
await signOut()
OnRamp.newSession()
}
App lifecycle
The SDK automatically tracks app foreground/background transitions using AppState. These are visible in session timelines and used to detect session boundaries.
You do not need to add any lifecycle listeners yourself.
TypeScript
The SDK ships full TypeScript types. No @types/ package needed.
import { OnRamp } from '@onramp-sdk/react-native'
function trackPayment(amount: number): void {
OnRamp.step('payment_completed', {
properties: { amount_cents: Math.round(amount * 100) },
})
}
Offline support
Events are batched and queued in AsyncStorage when the device is offline. The queue is flushed automatically when connectivity is restored. The queue holds up to 500 events; older events are dropped if this limit is exceeded without a connection.
