React Native
MetaRouter React Native SDK
Capture behavioral data directly from your React Native app and route it through MetaRouter to any destination — without juggling multiple vendor SDKs.
Why use the Mobile SDK?
One integration, unlimited destinations
Stop embedding separate SDKs for analytics, marketing, and data warehouses. Collect once, route everywhere through MetaRouter.
Update routing without app releases
Add or remove destinations server-side. No code changes, no app store review cycles.
Reliable delivery, even offline
Events are queued locally and delivered automatically when connectivity returns. Built-in retry logic handles transient failures. Offline mode supported for durable event persistence regardless of network conditions.
Automatic identity resolution
Track users from first app open through sign-up and beyond. The SDK manages anonymous IDs, persists identity across sessions, and connects pre-login activity to known users.
Privacy-ready by design
Built-in support for advertising ID consent, easy opt-out handling, and clean user data reset for GDPR/CCPA compliance.
Lightweight footprint
Minimal dependencies. Small impact on bundle size and startup time.
Quick start
1) Install the SDK
npm install @metarouter/react-native-sdk \
@react-native-async-storage/async-storage \
react-native-device-infoThen install the iOS pods and rebuild the native app:
cd ios && pod installThe SDK includes native iOS and Android modules for disk persistence and network monitoring, so a JavaScript-only reload is not enough after installing or upgrading. If the native modules are missing (for example in Expo Go), the SDK still runs but disk persistence is disabled, with a warning in the log.
2) Initialize
import { createAnalyticsClient } from "@metarouter/react-native-sdk";
// Call once at app startup, outside any component.
const analytics = createAnalyticsClient({
writeKey: "YOUR_WRITE_KEY",
ingestionHost: "https://YOUR_CLUSTER.mr-in.com",
});Your writeKey and ingestionHost are available in the MetaRouter dashboard.
createAnalyticsClient returns the client immediately — there is no promise to await. Initialization finishes in the background; calls made before it completes are queued and replayed in order, so the client is safe to use right away.
3) Start tracking
// Track user actions
analytics.track("Product Viewed", {
sku: "SHOE-123",
price: 89.99,
});
// Identify known users
analytics.identify("user-456", {
email: "[email protected]",
plan: "premium",
});
// Track screen views
analytics.screen("Product Detail", {
category: "Footwear",
});Events are batched and delivered automatically. No additional setup required.
Core capabilities
Event tracking
| Method | Purpose |
|---|---|
track(event, properties) | Capture user actions — purchases, clicks, feature usage |
screen(name, properties) | Record screen views for navigation analytics |
page(name, properties) | Record page views (web semantics, if applicable) |
User identity
| Method | Purpose |
|---|---|
identify(userId, traits) | Associate events with a known user |
group(groupId, traits) | Associate users with a company, team, or account |
alias(newUserId) | Link anonymous activity to a newly identified user |
getAnonymousId() | Returns the anonymous ID the SDK assigned to this device |
Lifecycle & privacy
| Method | Purpose |
|---|---|
flush() | Send queued events immediately |
reset() | Clear all user data (use on logout) |
setAdvertisingId(id) | Set IDFA/GAID for attribution (with user consent) |
clearAdvertisingId() | Remove advertising ID when user opts out |
recordOpenedURL(url, sourceApplication?) | Attach a deep link to the next Application Opened event (see Lifecycle Events) |
Debugging
| Method | Purpose |
|---|---|
enableDebugLogging() | Turn on verbose SDK logging at runtime (same as debug: true) |
getDebugInfo() | Returns the SDK's current state: lifecycle, queue length, identity, network status, and configError for a refused session |
setTracing(enabled) | Add a Trace: true header to every request, for troubleshooting with MetaRouter support |
Identity that just works
The SDK automatically handles the complexity of user identity:
Before login — Users are assigned a stable anonymous ID (UUID v4)
On login — Call identify() to attach a known user ID
Across sessions — Identity persists through app restarts via AsyncStorage
On logout — Call reset() to clear everything and start fresh
Connect anonymous browsing to authenticated users with alias():
// User browses anonymously, then signs up
analytics.track("Product Viewed", { sku: "ABC" }); // tracked as anonymous
// User creates account
analytics.alias("new-user-789"); // links anonymous → known
analytics.identify("new-user-789", { email: "[email protected]" });
// Full journey is now connected in your downstream toolsAdvertising & attribution
For ad attribution, you can include the platform advertising ID:
// Only after obtaining user consent
// iOS: IDFA | Android: GAID
analytics.setAdvertisingId(advertisingId);
// When user opts out
analytics.clearAdvertisingId();Privacy note: Always obtain explicit consent before collecting advertising IDs
iOS — Use the App Tracking Transparency framework to request permission
Android — Respect the user’s ad personalization settings
React Hooks
Use the provider and hook for easy access throughout your app:
import {
createAnalyticsClient,
MetaRouterProvider,
useMetaRouter,
} from "@metarouter/react-native-sdk";
// Create the client once, outside any component
const analytics = createAnalyticsClient({
writeKey: "YOUR_WRITE_KEY",
ingestionHost: "https://YOUR_CLUSTER.mr-in.com",
});
// In your App component
const App = () => (
<MetaRouterProvider analyticsClient={analytics}>
<YourApp />
</MetaRouterProvider>
);// In any component
const ProductScreen = () => {
const { analytics } = useMetaRouter();
const handlePurchase = () => {
analytics.track("Purchase Completed", { amount: 99.99 });
};
return <Button onPress={handlePurchase} title="Buy Now" />;
};Configuration options
The React Native SDK is configured via the options object passed to createAnalyticsClient():
import { createAnalyticsClient } from "@metarouter/react-native-sdk";
const analytics = createAnalyticsClient({
writeKey: "YOUR_WRITE_KEY", // Required
ingestionHost: "https://YOUR_CLUSTER.mr-in.com", // Required
flushIntervalSeconds: 10, // Optional (default: 10)
debug: false, // Optional (default: false)
maxQueueEvents: 2000, // Optional (default: 2000)
maxDiskEvents: 10000, // Optional (default: 10000)
trackLifecycleEvents: false, // Optional (default: false)
});| Option | Required | Type | Default | Description |
|---|---|---|---|---|
writeKey | Yes | string | — | Your MetaRouter write key. Must not be empty or whitespace-only. |
ingestionHost | Yes | string | — | Your MetaRouter ingestion endpoint: an http/https URL with a host. Surrounding whitespace and trailing slashes are removed. |
flushIntervalSeconds | No | number | 10 | How often the SDK attempts to send queued events. Values below 1 are clamped to 1. |
debug | No | boolean | false | Enables verbose SDK logging. Can also be toggled at runtime via analytics.enableDebugLogging(). |
maxQueueEvents | No | number | 2000 | Maximum events held in the in-memory queue. Values below 1 are clamped to 1. The queue is also bounded by a 5 MB byte cap. When either limit is reached, the queue moves to disk (or, with maxDiskEvents: 0, the oldest events are dropped). |
maxDiskEvents | No | number | 10000 | Maximum unsent events retained on disk for crash safety and offline recovery. Negative values are clamped to 0. Set to 0 to disable disk persistence and run as a pure in-memory pipeline (events are lost on app kill). |
trackLifecycleEvents | No | boolean | false | Automatically emit the four Application * lifecycle events. See Lifecycle Events below. Available since 1.8.0. |
onConfigError | No | function | — | Called synchronously by createAnalyticsClient when the options are invalid in a release build. See Invalid configuration below. Available since 1.8.0. |
Key implementation notes
- Events are batched in memory and sent on the flush interval. Queued events are written to disk when the app goes to the background, when the in-memory queue is full, and while the device is offline.
- When the device comes back online, and on the next launch, events on disk are sent straight to the network without being loaded back into memory. Events older than 7 days are dropped.
- When the disk store reaches
maxDiskEvents, the oldest events are dropped. - Setting
maxDiskEvents = 0disables disk persistence; oldest events are dropped when the in-memory queue is full. reset()deletes the events stored on disk.- Failed requests are retried with backoff. A
401,403or404from your ingestion host stops the SDK for the session and clears the queue; this does not callonConfigError.
Invalid configuration
Requires SDK 1.8.0+.
If the configuration is invalid — an empty write key, or an ingestion host that isn't an http(s) URL with a host — the SDK responds by build type when you call createAnalyticsClient:
- Development builds (
__DEV__) throwInvalid InitOptions: <reason>, so configuration mistakes surface immediately at the call site. - Release builds never crash your app: the SDK logs an error, calls your
onConfigErrorcallback if you set one, and stays inert for the session — no events are sent, and every API remains safe to call (getAnonymousId()resolves""andflush()resolves immediately instead of waiting). CallingcreateAnalyticsClientagain with valid options, orreset(), recovers the session.
Numeric options out of range (flushIntervalSeconds, maxQueueEvents, maxDiskEvents) are clamped to their nearest valid value with a logged warning rather than failing.
const analytics = createAnalyticsClient({
writeKey: remoteConfig.writeKey,
ingestionHost: remoteConfig.ingestionHost,
onConfigError: (error) => {
// error.type is "emptyWriteKey" or "invalidIngestionHost" (with error.raw)
crashReporter.log(`MetaRouter disabled: ${error.description}`);
},
});error.description is the same text the iOS and Android SDKs report, and getDebugInfo().configError carries it for a refused session, so misconfiguration is diagnosable from the same API as every other SDK state.
Lifecycle Events
Requires SDK 1.8.0+.
Turn on automatic tracking of the four standard application-lifecycle events — Application Installed, Application Updated, Application Opened, and Application Backgrounded — to anchor attribution, retention, and session reporting without instrumenting each one by hand. They flow through the same pipeline as your own track calls, and behave the same as on the iOS and Android SDKs.
Enable it
Lifecycle events are opt-in. Set trackLifecycleEvents: true:
const analytics = createAnalyticsClient({
writeKey: "YOUR_WRITE_KEY",
ingestionHost: "https://YOUR_CLUSTER.mr-in.com",
trackLifecycleEvents: true,
});With the flag left at its default (false), the SDK emits none of these events and recordOpenedURL(...) is ignored with a warning. Upgrading the SDK never turns them on silently — an app has to opt in.
The events
| Event | When it fires | Properties |
|---|---|---|
Application Installed | First launch after a fresh install (no prior identity or lifecycle state on the device) | version, build |
Application Updated | First launch after the app's version/build changes — also the first launch after upgrading from a pre-lifecycle SDK build | version, build, previous_version, previous_build ("unknown" for the SDK-upgrade case) |
Application Opened | Cold launch while active, and each return from the background | version, build, from_background (false on cold launch, true on return), plus url / referring_application when a deep link was recorded |
Application Backgrounded | App enters the background | — |
On cold launch, Application Installed/Updated fire before Application Opened, so attribution sees the install or update ahead of the session start. Foreground and background transitions come from React Native's AppState. Only a real return from the background emits Application Opened — brief interruptions that leave the app merely inactive (Control Center, a system prompt, Face ID) do not.
If the process is launched into the background (silent push, headless task), the cold-launch Application Opened is deferred until the first real activation, then emitted with from_background: false.
Install/update state is stored per-device (in AsyncStorage, under keys separate from identity) — calling reset() clears the user but keeps the install/update history.
Deep links
The SDK doesn't read Linking or auto-capture deep links; you forward the URL so it can be attached to the next Application Opened event.
| Method | Purpose |
|---|---|
recordOpenedURL(url: string, sourceApplication?: string) | Record a deep-link URL to attach to the next Application Opened event. |
Forward both the cold-launch URL and the URLs that arrive while the app is running:
import { Linking } from "react-native";
// Cold launch — safe to call right after createAnalyticsClient();
// the URL reaches the cold-launch Application Opened.
Linking.getInitialURL().then((url) => {
if (url) analytics.recordOpenedURL(url);
});
// While running — attached to the next Application Opened.
Linking.addEventListener("url", ({ url }) => {
analytics.recordOpenedURL(url);
});URLs from outside Linking (a deep-link library such as Branch, or a native handler) are forwarded the same way; pass the referring app as the second argument when you have it.
The next Application Opened the SDK emits carries url (and referring_application when you pass a source). The buffer is one-shot — cleared once that event fires — and last-write-wins if you record more than one URL before it.
Deep-link URLs often carry secrets — auth tokens, OTPs, magic-link codes. The SDK forwards the URL as-is and does not sanitize it. Strip sensitive query parameters before callingrecordOpenedURL(...).
Requirements
| Component | Version |
|---|---|
| React Native | 0.73+ |
| React | 16.8+ |
| iOS | 13.0+ |
| Android | API 23+ (6.0 Marshmallow) |
Peer dependencies
| Package | Version |
|---|---|
@react-native-async-storage/async-storage | 2.2.0+ |
react-native-device-info | 14.0.4+ |
Updated 1 day ago