@metricflow-ai/react-native
v0.1.0
Published
Advanced analytics SDK for React Native applications with event tracking, crash reporting, and performance monitoring
Readme
Metricflow Analytics React Native SDK
Analytics SDK for React Native applications with automatic device/app/session properties, event tracking, batching, retry logic, region routing, crash tracking, and local persistence.
Additional Guides
- Touch events deep-dive: README_TOUCH_EVENTS.md
Overview
The Metricflow Analytics React Native SDK provides a full analytics pipeline for React Native apps:
- Event tracking with
eventId,sessionId, timestamp, and region metadata - User identification (
identify) with persisted traits - Automatic SuperProperties — device, OS, app, screen, network, battery info merged into every event
- Automatic session tracking —
App Openedevents and background queue flushing - Crash tracking — unhandled JS errors and promise rejections captured automatically
- In-memory event queue with periodic and batch-size flush
- Gzip compression for event payloads
- Retry logic with exponential backoff
- Local persistence via AsyncStorage
- Opt-in/opt-out tracking controls
- Auto region detection via IP geolocation (
automode) - Custom endpoint override (useful for development/staging)
Installation
npm install @metricflow-ai/react-nativeOptional Peer Dependencies
These libraries must be installed in your app (not in the SDK itself). The SDK declares them as optional peer dependencies — it works without them, but you get richer auto-collected properties when they are present.
# Install in your app project
npm install react-native-device-info
npm install @react-native-community/netinfoAfter installing, rebuild your app for native linking to take effect:
# Android
cd android && ./gradlew clean && cd ..
npx react-native run-android
# iOS
cd ios && pod install && cd ..
npx react-native run-iosWhat you get with each library:
| Library | Properties Added |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| react-native-device-info | model, manufacturer, brand, deviceId, isTablet, isEmulator, hasNfc, hasTelephone, appVersion, buildNumber, bundleId, appName, firstInstallTime, lastUpdateTime, batteryLevel, totalMemory, freeDiskStorage, carrier |
| @react-native-community/netinfo | networkType, isWifi |
Without these libraries the SDK runs normally — device/network properties are silently omitted from events.
No uuid or crypto.getRandomValues polyfill is required. The SDK uses a React Native/Hermes-safe ID generator internally.
Requirements
- Node.js >= 16
- npm >= 8
- React Native >= 0.73
Quick Start
import { metricflowAnalytics } from "@metricflow-ai/react-native";
// Initialize once at app startup
await metricflowAnalytics.init({
appId: "your-app-id",
secretKey: "your-secret-key",
debug: __DEV__,
});
// Track custom events
metricflowAnalytics.track("button_clicked", {
button_id: "subscribe",
screen: "paywall",
});
// Track screen views
metricflowAnalytics.screen("HomeScreen");
// Identify user
metricflowAnalytics.identify("user_123", {
name: "Alex Kumar",
email: "[email protected]",
first_name: "Alex",
last_name: "Kumar",
plan: "premium",
});
// Set persistent user properties (merged into all future events)
metricflowAnalytics.setUserProperties({ ab_group: "variant_b" });
// Manually flush the event queue
await metricflowAnalytics.flush();Autocapture Setup
Autocapture gives Heap-style baseline tracking for taps, swipes, and screen views without adding track() calls to every button.
1) Enable autocapture after init
import { metricflowAnalytics } from "@metricflow-ai/react-native";
await metricflowAnalytics.init({
appId: "your-app-id",
secretKey: "your-secret-key",
endpoint: "https://your-api-domain.com/api/track",
debug: __DEV__,
});
metricflowAnalytics.enableAutocapture({
captureTouch: true,
captureSwipes: true,
captureScreenViews: true,
captureText: true,
captureHierarchy: true,
captureCoordinates: false,
});2) Wrap your app with AutocaptureProvider
Pass the same React Navigation ref to NavigationContainer and AutocaptureProvider.
import React, { useEffect } from "react";
import { NavigationContainer, useNavigationContainerRef } from "@react-navigation/native";
import { AutocaptureProvider, metricflowAnalytics } from "@metricflow-ai/react-native";
type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
Settings: undefined;
};
export default function App(): React.JSX.Element {
const navigationRef = useNavigationContainerRef<RootStackParamList>();
useEffect(() => {
void metricflowAnalytics
.init({
appId: "your-app-id",
secretKey: "your-secret-key",
endpoint: "https://your-api-domain.com/api/track",
debug: __DEV__,
})
.then(() => {
metricflowAnalytics.enableAutocapture({
captureTouch: true,
captureSwipes: true,
captureScreenViews: true,
captureText: true,
captureHierarchy: true,
captureCoordinates: false,
});
});
}, []);
return (
<AutocaptureProvider navigationRef={navigationRef}>
<NavigationContainer ref={navigationRef}>
{/* Your navigators/screens */}
</NavigationContainer>
</AutocaptureProvider>
);
}Autocaptured Events
Tap and swipe interactions are sent as:
autocaptureExample properties:
{
"screen_name": "Home",
"interaction_type": "tap",
"element_text": "Continue",
"hierarchy": "HomeScreen > Pressable",
"component_type": "Pressable",
"target_tag": 72
}Swipe events include:
{
"interaction_type": "swipe",
"swipe_direction": "left",
"gesture_distance": 80
}Screen changes are sent as:
screen_viewwith screen_name and, when available, previous_screen.
Privacy Controls
Use MetricflowIgnore to exclude sensitive UI from autocapture:
import { MetricflowIgnore } from "@metricflow-ai/react-native";
<MetricflowIgnore>
<PasswordInput />
</MetricflowIgnore>;To capture the interaction but suppress text inside the subtree:
<MetricflowIgnore allowInteraction>
<PinPad />
</MetricflowIgnore>Autocapture respects optOutTracking(). If the user opts out, autocaptured events are not queued.
Public API
Main export: metricflowAnalytics
| Method | Description |
| ------------------------------- | -------------------------------------------------------------- |
| init(config) | Initialize the SDK. Must be called before any other method. |
| track(eventName, properties?) | Track a custom event. |
| screen(screenName, props?) | Track a screen view event. |
| identify(userId, traits?) | Set the current user identity. |
| alias(newId) | Create an alias for the current user. |
| setUserProperties(props) | Set persistent properties merged into all future events. |
| setDebug(enabled) | Enable or disable debug logging at runtime. |
| enableAutocapture(options?) | Enable automatic tap, swipe, and screen view tracking. |
| disableAutocapture() | Disable automatic interaction and screen view tracking. |
| optOutTracking() | Stop all event tracking for the current user. |
| optInTracking() | Resume event tracking. |
| reset() | Clear all user identity, super properties, and queued events. |
| timeEvent(eventName) | Start timing an event (placeholder — duration support coming). |
| flush() | Manually flush the event queue to the API. |
User Identity And Profile Properties
Call identify() after login/signup when you know the logged-in user's id. In the current Metricflow user list flow, include name and email in the traits object so the Users table can show a readable profile instead of only the user id.
metricflowAnalytics.identify("user_123", {
name: "Alex Kumar",
email: "[email protected]",
first_name: "Alex",
last_name: "Kumar",
});nameis used as the primary display name in the Users table. Send the combined full name here, for examplefirst_name + " " + last_name.emailis used as the primary email in the Users table.first_nameandlast_nameare optional profile fields, but send them when your app stores names separately.- If your app stores first and last name separately, derive
namefromfirst_nameandlast_namebefore callingidentify. - You can call
identify()again after a profile update with the latestnameandemail. - Use
setUserProperties()for extra persistent properties such as plan, role, experiment, or account type. Do not rely on it as the only place where you send displaynameandemail.
const name = [user.first_name, user.last_name].filter(Boolean).join(" ").trim();
metricflowAnalytics.identify(user.id, {
email: user.email,
...(name ? { name } : {}),
first_name: user.first_name,
last_name: user.last_name,
});Recommended login flow:
await metricflowAnalytics.init({
appId: "your-app-id",
secretKey: "your-secret-key",
});
const name = [user.first_name, user.last_name].filter(Boolean).join(" ").trim();
metricflowAnalytics.identify(user.id, {
email: user.email,
...(name ? { name } : {}),
first_name: user.first_name,
last_name: user.last_name,
});
metricflowAnalytics.track("login_success");Configuration
interface Config {
appId: string; // Required. Your public project app_id.
secretKey: string; // Required. Sends x-secret-key for SDK authentication.
endpoint?: string; // Optional. Custom API endpoint, for example staging/local/ngrok.
batchSize?: number; // Default: 20
flushInterval?: number; // Default: 30000 (ms)
optOutByDefault?: boolean; // Default: false
enableGzip?: boolean; // Default: true
enableCrashTracking?: boolean; // Default: true
sessionTimeout?: number; // Default: 1800000 (30 minutes, min: 60000)
debug?: boolean; // Default: false
}Init Config
Use init() once when the app starts, before calling track, screen, identify, or flush.
await metricflowAnalytics.init({
appId: "scamguradlocaltesting_062fb12c216a",
secretKey: "your-secret-key",
endpoint: "https://your-api-domain.com/api/track",
debug: __DEV__,
});Pass these values:
| Field | Required | Meaning |
| ----- | -------- | ------- |
| appId | Yes | Public project/app id. This is sent as x-metricflow-app-id. |
| secretKey | Yes | Secret SDK key. This is sent as x-secret-key so the backend can authenticate the mobile SDK. |
| endpoint | No | Track API URL. Use this for local, staging, ngrok, or custom backend. Defaults to MetricFlow's configured endpoint. |
| debug | No | Enables SDK logs during development. |
| batchSize | No | Number of queued events before automatic flush. |
| flushInterval | No | Time interval in milliseconds for automatic flush. |
| sessionTimeout | No | Inactivity window before a new session starts. Default is 30 minutes. |
writeKey is no longer used by the React Native SDK. Use secretKey instead.
Auto-Collected Properties (SuperProperties)
The SDK automatically collects and merges these into every event:
Always collected:
os,osVersion— platform and OS versionlibraryVersion— SDK versionscreenWidth,screenHeight,dpi,orientationtimezone,languagenetworkType,isWifi(requires@react-native-community/netinfo)
With react-native-device-info installed:
model,manufacturer,brand,deviceIdisTablet,isEmulator,hasNfc,hasTelephoneappVersion,buildNumber,bundleId,appNamefirstInstallTime,lastUpdateTimebatteryLevel,totalMemory,freeDiskStorage,carrier
You can also set custom super properties that persist across sessions:
metricflowAnalytics.setUserProperties({ user_tier: "gold", experiment: "v2" });Event Properties
Every event sent by the SDK has two levels:
- Top-level event fields (event identity)
propertiesobject (context and custom details)
1) Top-level event fields
eventId: Unique ID for this one event.eventName: What happened. Example:App Opened,Screen View,signup_started.userId: Which user did this action. If user is not identified yet, it may beanonymous.sessionId: Which app session this event belongs to.timestamp: Event time in milliseconds.region: Which routing region is used (eu,india, ordefault).
2) properties fields
These are extra details attached to the event.
Device and OS
os: Device platform (androidorios).osVersion: OS version string.model: Device model (example:Redmi Note 8 Pro).manufacturer: Device maker (example:Xiaomi).brand: Device brand (example:Redmi).deviceId: Device unique ID from device-info library.isTablet:trueif tablet, elsefalse.isEmulator:trueif emulator, elsefalse.
App Info
appVersion: App version name.buildNumber: App build number.bundleId: App package/bundle ID.appName: App display name.firstInstallTime: First install time in milliseconds.lastUpdateTime: Last update time in milliseconds.
Screen and Display
screenWidth: Screen width.screenHeight: Screen height.dpi: Screen density.orientation:portraitorlandscape.
Network and Locale
networkType: Current connection type (example:wifi,cellular,unknown).isWifi:trueif current type is Wi-Fi.timezone: Device timezone (example:Asia/Kolkata).language: Device/app locale (example:en-IN).
Hardware and Capacity
batteryLevel: Battery level (0 to 1).totalMemory: Total device memory in bytes.freeDiskStorage: Free storage in bytes.carrier: Network carrier name. If unavailable, fallback can be empty/unknown.hasNfc: NFC support (when available).hasTelephone: Telephony support (when available).
Session and Tracking-specific fields
session_id: Added onApp Openedevent. Same value as top-levelsessionIdfor readability.is_first_open:trueon first-ever app open for this installation.screen_name: Added onScreen Viewevent.
Custom properties (from your app)
Anything you pass in track(eventName, properties) is added here.
Examples:
method: "google"for signup methodplan_type: "monthly"for subscriptioncurrency: "USD",amount: "9.99"
Automatic Events
| Event | Fired When |
| ----------------------------- | ------------------------------------------------------------------ |
| App Opened | App launches or comes to foreground (new session) |
| JS Exception | Unhandled JavaScript error (requires enableCrashTracking: true) |
| Unhandled Promise Rejection | Unhandled promise rejection (requires enableCrashTracking: true) |
License
MIT — see LICENSE.
