OpenObserve Docs
User GuideData ExplorationReal User Monitoring (RUM)Mobile RUM

React Native RUM Integration

This guide walks through adding OpenObserve Real User Monitoring to a React Native app end to end: installing the SDK, initializing it at startup, turning on automatic instrumentation for screens, taps, and network calls, reporting errors and crashes, and controlling privacy and data volume. The React Native SDK sits on top of the native OpenObserve Android and iOS SDKs, so your JavaScript app gets native-grade crash reporting and instrumentation without leaving React Native.

New to mobile RUM in general? Start with the Mobile RUM Overview for the concepts (sessions, views, actions, resources, errors) that this guide assumes.

!!! warning "Alpha status"

The React Native SDK is currently published as `0.1.0-alpha.5`. It is ready to integrate and evaluate — pin the exact version and test upgrades, since some configuration details may change before the stable release.

What you get

Once integrated, OpenObserve RUM automatically captures:

  • Screens (views) — via React Navigation, react-native-navigation, or manual calls.
  • User actions — taps, scrolls, swipes, and back gestures, named from accessibility labels or element props.
  • Network resourcesfetch and XMLHttpRequest calls with timing, status, and size, plus distributed tracing to your first-party backends.
  • JavaScript errors — unhandled JS exceptions with stack traces.
  • Native crashes — iOS and Android crashes, when native crash reporting is enabled.
  • Mobile vitals — long tasks, app hangs, ANRs, memory warnings, and frustration signals.

Prerequisites

  • A React Native app (react >= 16.13.1, react-native >= 0.63.4).
  • An OpenObserve instance — OpenObserve Cloud or self-hosted. For local testing, one container is enough:
docker run -d --name openobserve \
  -p 5080:5080 \
  -e ZO_ROOT_USER_EMAIL="root@example.com" \
  -e ZO_ROOT_USER_PASSWORD="Complexpass#123" \
  public.ecr.aws/zinclabs/openobserve:latest
  • Your client token, RUM application id, and ingestion endpoint from Data → Data Sources → Real User Monitoring in the OpenObserve UI.
  • CocoaPods for iOS builds.

Step 1 — Install the SDK

Add the core package:

npm install @openobserve/mobile-react-native
# or
yarn add @openobserve/mobile-react-native

Install iOS native pods (Android autolinks, no extra step):

npx pod-install
# or: cd ios && pod install

Optionally add companion packages for the features you need:

# Automatic screen tracking for React Navigation
npm install @openobserve/mobile-react-navigation

# Session Replay
npm install @openobserve/mobile-react-native-session-replay

Step 2 — Initialize OpenObserve RUM

Configure the SDK once, as early as possible in your app's lifecycle, and wrap your app tree in OpenObserveProvider. Build the configuration with OpenObserveProviderConfiguration:

// App.tsx
import React from 'react';
import {
  OpenObserveProvider,
  OpenObserveProviderConfiguration,
  TrackingConsent,
  SdkVerbosity,
  BatchSize,
  UploadFrequency,
} from '@openobserve/mobile-react-native';

const OPENOBSERVE_ENDPOINT = 'https://your-openobserve-instance:5080';

const config = new OpenObserveProviderConfiguration(
  'YOUR_CLIENT_TOKEN',       // clientToken from Data → Data Sources → Real User Monitoring
  'production',              // env
  TrackingConsent.GRANTED,  // pending | granted | not_granted
  {
    batchSize: BatchSize.MEDIUM,
    uploadFrequency: UploadFrequency.AVERAGE,
    rumConfiguration: {
      applicationId: 'YOUR_APPLICATION_ID',
      customEndpoint: OPENOBSERVE_ENDPOINT,
      // Automatic instrumentation — off by default, turn on what you want:
      trackInteractions: true,
      trackResources: true,
      trackErrors: true,
      sessionSampleRate: 100,
      nativeCrashReportEnabled: true,
      // Link app requests to backend traces:
      firstPartyHosts: [{ match: 'api.example.com', propagatorTypes: ['tracecontext'] }],
    },
    logsConfiguration: { customEndpoint: OPENOBSERVE_ENDPOINT },
    traceConfiguration: { customEndpoint: OPENOBSERVE_ENDPOINT },
  },
);
config.service = 'com.example.app';
config.version = '1.0.0';
config.verbosity = SdkVerbosity.DEBUG; // remove or lower in production

export default function App() {
  return (
    <OpenObserveProvider configuration={config}>
      {/* your navigation container and app tree */}
    </OpenObserveProvider>
  );
}

That single provider initializes the SDK, enables RUM, and starts the automatic instrumentation you turned on. Data now flows to your OpenObserve instance.

!!! note "Consent gating"

Nothing is collected until tracking consent is `granted`. If you show a consent dialog, initialize with `TrackingConsent.PENDING` and call `OoSdkReactNative.setTrackingConsent(TrackingConsent.GRANTED)` once the user agrees. See [Security & Privacy](./security-privacy.md).

Step 3 — Configuration options

Core options

OptionTypeDefaultDescription
clientTokenstring— (required)Ingestion credential from Data → Data Sources → Real User Monitoring.
envstring— (required)Environment tag, e.g. production, staging.
trackingConsentTrackingConsentgrantedpending, granted, or not_granted.
servicestringapp bundle idService/app identifier used to group data.
versionstringapp versionRelease version — used for release health.
batchSizeBatchSizeMEDIUMSMALL / MEDIUM / LARGE — how much to buffer per upload.
uploadFrequencyUploadFrequencyAVERAGEFREQUENT / AVERAGE / RARE — how often to upload.
verbositySdkVerbosityoffSDK internal log level (debug/info/warn/error).
proxyConfigurationProxyConfigurationRoute ingestion through a proxy.

RUM options (rumConfiguration)

OptionTypeDefaultDescription
applicationIdstring— (required)RUM application id from Data → Data Sources → Real User Monitoring.
customEndpointstringYour OpenObserve instance base URL.
trackInteractionsbooleanfalseAuto-capture taps/gestures as actions.
trackResourcesbooleanfalseAuto-capture fetch/XHR network requests.
trackErrorsbooleanfalseAuto-capture unhandled JavaScript errors.
sessionSampleRatenumber100Percentage of sessions to keep (0–100).
resourceTraceSampleRatenumber100Percentage of resources to trace end-to-end.
nativeCrashReportEnabledbooleanfalseCapture native iOS/Android crashes.
nativeViewTrackingbooleanfalseTrack native views (UIKit/Activity) in addition to JS.
longTaskThresholdMsnumber | false0JS long-task threshold; false disables.
nativeLongTaskThresholdMsnumber200Native long-task/ANR threshold.
trackFrustrationsbooleantrueDetect rage taps and other frustration signals.
trackBackgroundEventsbooleanfalseRecord events while the app is backgrounded.
vitalsUpdateFrequencyVitalsUpdateFrequencyAVERAGEMobile-vitals sampling cadence (NEVER to disable).
firstPartyHostsFirstPartyHost[][]Hosts to inject trace headers for.
actionEventMapper / errorEventMapper / resourceEventMapperfunction | nullnullScrub or drop events before send.

Step 4 — Track screens (views)

With React Navigation

Install @openobserve/mobile-react-navigation and start tracking once you have a navigation container ref:

import { useRef } from 'react';
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
import { OoRumReactNavigationTracking } from '@openobserve/mobile-react-navigation';

function Root() {
  const navigationRef = useNavigationContainerRef();

  return (
    <NavigationContainer
      ref={navigationRef}
      onReady={() => {
        OoRumReactNavigationTracking.startTrackingViews(navigationRef);
      }}
    >
      {/* screens */}
    </NavigationContainer>
  );
}

This starts and stops a RUM view automatically on every navigation, including Android hardware back-press exits. Stop tracking with OoRumReactNavigationTracking.stopTrackingViews(navigationRef).

Manually

When automatic tracking does not fit (custom navigators, modals, tab changes you want named yourself), drive views directly:

import { OoRum } from '@openobserve/mobile-react-native';

await OoRum.startView('checkout', 'Checkout');
// ...user is on the screen...
await OoRum.stopView('checkout');

Step 5 — Track user actions

With trackInteractions: true, taps and gestures are captured automatically and named from the element's accessibilityLabel (or a custom actionNameAttribute prop you configure). To record actions explicitly:

import { OoRum, RumActionType } from '@openobserve/mobile-react-native';

await OoRum.addAction(RumActionType.TAP, 'Add to cart', { productId: 'sku-42' });

RumActionType values: TAP, SCROLL, SWIPE, BACK, CUSTOM.

Step 6 — Track network requests

With trackResources: true, the SDK proxies fetch and XMLHttpRequest and records each call as a RUM resource with URL, method, status, size, and timing. For requests to hosts you list in firstPartyHosts, it injects W3C tracecontext headers so the resource links to the backend trace in OpenObserve — giving you one continuous trace from tap to server.

To record a resource manually (for a non-HTTP transport, for example):

import { OoRum } from '@openobserve/mobile-react-native';

await OoRum.startResource('req-1', 'GET', 'https://api.example.com/cart');
// ...request completes...
await OoRum.stopResource('req-1', 200, 'fetch', 1024);

propagatorTypes for first-party hosts can be tracecontext (W3C — what OpenObserve reads), b3, or b3multi.

Step 7 — Report errors

With trackErrors: true, unhandled JavaScript exceptions are captured automatically. Report handled errors yourself so you can see failures that you caught and recovered from:

import { OoRum, ErrorSource } from '@openobserve/mobile-react-native';

try {
  await checkout();
} catch (e) {
  await OoRum.addError(
    (e as Error).message,
    ErrorSource.SOURCE,
    (e as Error).stack ?? '',
    { screen: 'checkout' },
  );
}

ErrorSource values: NETWORK, SOURCE, CONSOLE, WEBVIEW, CUSTOM. For crash symbolication and error grouping, see Error & Crash Tracking.

Step 8 — User identity and global context

Attach the logged-in user so you can measure user-level impact (respecting consent). id is required:

import { OoSdkReactNative } from '@openobserve/mobile-react-native';

// after login
OoSdkReactNative.setUserInfo({
  id: 'user-123',
  name: 'Ada Lovelace',
  email: 'ada@example.com',
  plan: 'premium',
});

// on logout
OoSdkReactNative.clearUserInfo();

Add global attributes that attach to every RUM event, log, and span — useful for release channel, feature flags, or A/B buckets:

OoSdkReactNative.addAttribute('feature_flag.new_checkout', true);
OoSdkReactNative.addAttribute('build.channel', 'beta');

Step 9 — Logs (optional)

The same SDK forwards structured logs to OpenObserve:

import { OoLogs } from '@openobserve/mobile-react-native';

OoLogs.info('Checkout started', { cartValue: 89.9 });
OoLogs.error('Payment declined', { reason: 'insufficient_funds' });

Logs are correlated with the active RUM session, so you can pivot from a session to its logs in OpenObserve.

Step 10 — Session Replay (optional)

Add @openobserve/mobile-react-native-session-replay to record privacy-first playback of user sessions:

import { SessionReplay, TextAndInputPrivacyLevel, TouchPrivacyLevel } from '@openobserve/mobile-react-native-session-replay';

await SessionReplay.enable({
  replaySampleRate: 20, // replay is heavier — sample lower than sessions
  textAndInputPrivacyLevel: TextAndInputPrivacyLevel.MASK_ALL,
  touchPrivacyLevel: TouchPrivacyLevel.HIDE,
});

Wrap any subtree you never want recorded in <OoPrivacyView>. Replay defaults are privacy-preserving (all text and inputs masked, touches hidden). See Security & Privacy for the full privacy model.

Performance and overhead

The SDK is designed to stay out of your app's way:

  • Off the main thread. Collection, batching, and upload happen in the background.
  • Batched and buffered. Events are grouped and written to disk, then uploaded on the uploadFrequency cadence; if the device is offline, they wait and retry rather than being lost.
  • You control volume. sessionSampleRate, resourceTraceSampleRate, replaySampleRate, and larger batchSize / rarer uploadFrequency all reduce network and battery use.
  • Session Replay is the heaviest feature — keep its sample rate well below your session sample rate.

See Best Practices for production tuning and cost control.

Verify it works

  1. Run the app on a device or simulator (a debug build is fine).
  2. Navigate a few screens, tap around, and trigger a network request.
  3. In OpenObserve, open RUM and confirm your session appears with views, actions, and resources.
  4. Force a test error and confirm it shows under Error Tracking.

If nothing appears, check the SDK logs (SdkVerbosity.DEBUG), verify the customEndpoint matches your instance, confirm the client token and application id are correct, and make sure tracking consent is granted.

Troubleshooting

  • No data in OpenObserve. Confirm customEndpoint is reachable from the device, the client token is valid, and consent is granted. Enable SdkVerbosity.DEBUG and watch the native logs.
  • iOS build fails after install. Run npx pod-install again; make sure your Podfile platform is iOS 12+.
  • Screens not tracked. Ensure startTrackingViews runs after the navigation container is ready, or switch to manual startView/stopView.
  • Stack traces are minified. Upload JavaScript source maps and native mapping/symbol files for the release build — see Error & Crash Tracking.
  • Too much data / cost. Lower sessionSampleRate and replaySampleRate; see Best Practices.

What's next

Frequently asked questions

Which package do I install for React Native RUM?

The core package is @openobserve/mobile-react-native. It wraps the native Android and iOS SDKs, so you get native crash reporting, native view tracking, and native network instrumentation underneath the JavaScript layer. Optional companion packages add React Navigation tracking (@openobserve/mobile-react-navigation), Session Replay (@openobserve/mobile-react-native-session-replay), and more.

Do I need to run pod install after adding the SDK?

Yes, on iOS. The React Native package brings native iOS pods that are linked via autolinking, so after npm install you run 'npx pod-install' (or 'cd ios && pod install'). Android autolinks with no extra step. If you use the New Architecture, the SDK supports it when RCT_NEW_ARCH_ENABLED=1.

Where do I get the client token and application id?

Both come from Data → Data Sources → Real User Monitoring in your OpenObserve instance. The client token authenticates ingestion and the application id identifies your app in RUM. You pass the client token to OpenObserveProviderConfiguration and the application id to rumConfiguration.applicationId, and point rumConfiguration.customEndpoint at your OpenObserve instance base URL.

How do I track screens with React Navigation?

Install @openobserve/mobile-react-navigation and call OoRumReactNavigationTracking.startTrackingViews(navigationRef) once you have a navigation container ref. It automatically starts and stops RUM views as the user navigates, including Android hardware back-press exits. You can also track views manually with OoRum.startView / OoRum.stopView.

Does the React Native SDK capture native crashes?

Yes, when you set nativeCrashReportEnabled: true in rumConfiguration. That enables the underlying native crash reporters on both platforms, so native iOS and Android crashes are captured in addition to JavaScript errors. To get readable stack traces you upload JavaScript source maps and native symbol/mapping files as part of your release build.

How do I reduce the volume of data sent to OpenObserve?

Use sessionSampleRate (0–100) to keep a percentage of sessions, resourceTraceSampleRate to sample distributed traces, and — if you enable Session Replay — a separate replaySampleRate. You can also raise batchSize and lower uploadFrequency to batch more aggressively, and use event mappers to drop or redact specific events before they leave the device.

Can I attach the logged-in user to RUM sessions?

Yes. Call OoSdkReactNative.setUserInfo({ id, name, email, ...extra }) after login (id is required) and OoSdkReactNative.clearUserInfo() on logout. User info is attached to all subsequent RUM events, logs, and traces. Only collect identifying data with the appropriate consent — set TrackingConsent accordingly.

Was this page helpful?

Last updated on

On this page