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

bash
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.

tsx
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

OptionTypeRequiredDefaultDescription
apiKeystringYes-Your API key from Settings
appVersionstringNo-App version string, e.g. "2.4.1" - shows in the version breakdown
sessionTimeoutMsnumberNo1_800_000Idle time (ms) before a new session starts (default 30 min)
hoststringNohttps://ingest.getonramp.devOverride ingestion endpoint (for self-hosting)
captureInstallReferrerbooleanNofalseCapture 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.

ts
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

OptionTypeDescription
propertiesRecord<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:

tsx
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.

ts
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:

ts
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:

ts
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:

ts
// 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:

ts
OnRamp.setAttribution({
  provider: 'my_mmp',
  source: result.network,
  medium: result.channel,
  campaign: result.campaign,
  term: result.adGroup,
  content: result.creative,
})
MMPCallback fields mapped by OnRamp
AppsFlyermedia_source, af_channel, campaign, af_adset, af_ad
Adjustnetwork, campaign, adgroup, creative
Branch~feature, ~channel, ~campaign, ~tags, ~creative_name
Singularsource, campaign_name, adgroup_name, creative_name
Kochavanetwork_name, campaign_name, ad_group_name, creative_name
Airbridgechannel, campaign, ad_group, ad_creative
Tenjinad_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.

ts
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.

ts
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.