openobserve-react-native-rum
v0.1.0
Published
OpenObserve Real User Monitoring (RUM) SDK for React Native — automatic tap tracking, fetch interception, error capture, and view tracking with zero application code changes.
Downloads
95
Maintainers
Readme
openobserve-react-native-rum
OpenObserve Real User Monitoring (RUM) SDK for React Native.
Track user taps, navigation, network requests, JS errors, and app lifecycle events — automatically, with zero changes to your application code.
Features
| Feature | Automatic | Manual |
|---|---|---|
| User taps (onPress, onLongPress) | Yes | addAction() |
| Navigation / Views | Expo Router & React Navigation helpers | trackView() |
| Fetch / XHR | Yes | — |
| JS Errors | Yes | addError() |
| App Foreground / Background | Yes | — |
| User identity | — | setUser() |
| Custom context | — | setGlobalContext() |
Installation
# npm
npm install openobserve-react-native-rum
# yarn
yarn add openobserve-react-native-rum
# pnpm
pnpm add openobserve-react-native-rumNo native modules required.
Works in Expo Go, Expo Dev Client, and bare React Native.
Quick Start
1. Import FIRST and initialize
The SDK auto-installs all interceptors at import time (module side effect). This means the import must appear before any other component so the JSX runtime patches are in place before the first render.
// app/_layout.tsx (Expo Router) — or —
// index.js (bare RN) — or —
// App.tsx (any)
// ⬇️ MUST be the first import
import { OpenObserveRum } from 'openobserve-react-native-rum';
import { AppRegistry } from 'react-native';
import App from './App';
OpenObserveRum.init({
clientToken: 'YOUR_O2_API_KEY',
applicationId: 'YOUR_APP_ID',
site: 'openobserve.example.com', // without protocol
service: 'my-app',
env: 'production', // optional, default 'production'
version: '1.0.0', // optional
// organizationIdentifier: 'default',
// insecureHTTP: false,
// debug: true, // enable verbose logging
});
AppRegistry.registerComponent('main', () => App);That's it! All taps, fetches, errors, and app-state changes are tracked automatically.
2. Track navigation (optional but recommended)
The SDK does not depend on any router — instead it exposes trackView(name) that you call from your navigation listener.
Expo Router
import { OpenObserveRum } from 'openobserve-react-native-rum';
import { usePathname, useSegments } from 'expo-router';
import { useEffect } from 'react';
export default function RootLayout() {
const pathname = usePathname();
const segments = useSegments();
useEffect(() => {
if (pathname) {
OpenObserveRum.trackView(pathname);
}
}, [pathname, segments]);
return ( /* ... */ );
}React Navigation (v5 / v6)
import { OpenObserveRum } from 'openobserve-react-native-rum';
import { NavigationContainer } from '@react-navigation/native';
import { useRef } from 'react';
export default function App() {
const routeNameRef = useRef<string>();
return (
<NavigationContainer
onReady={() => {
routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name;
}}
onStateChange={() => {
const currentRoute = navigationRef.current?.getCurrentRoute()?.name;
if (currentRoute && currentRoute !== routeNameRef.current) {
OpenObserveRum.trackView(currentRoute);
routeNameRef.current = currentRoute;
}
}}
>
{/* screens */}
</NavigationContainer>
);
}3. Identify the user (optional)
OpenObserveRum.setUser('user-123', 'Jane Doe');4. Add custom context (optional)
OpenObserveRum.setGlobalContext({
plan: 'premium',
abTestGroup: 'B',
});How Automatic Tracking Works
User Interactions (taps)
The SDK patches the JSX runtime functions so every component with an onPress, onPressIn, or onLongPress prop is automatically wrapped. No accessibilityLabel or testID is required — the SDK extracts the action name from:
accessibilityLabeltestID- Visible text content of
children(recursively) - Component
displayName/name
JSX Transform Compatibility
React compiles JSX (<View />) into function calls. Which function depends on the JSX transform configured in your bundler:
| JSX Transform | Functions patched | When used |
|---|---|---|
| Automatic (default since React 17) | jsxDEV (dev), jsx / jsxs (prod) | Expo SDK ≥ 47, RN ≥ 0.72, CRA ≥ 4 |
| Classic | React.createElement | Older projects, or explicit runtime: 'classic' in Babel |
The SDK patches ALL of them, so it works regardless of which transform your project uses.
Look at your babel.config.js:
// Automatic (default in modern Expo / RN)
module.exports = {
presets: ['babel-preset-expo'],
// or
presets: ['module:metro-react-native-babel-preset'],
};
// → Uses automatic transform. jsxDEV (dev) / jsx+jsxs (prod) will be patched.// Classic (explicitly set)
module.exports = {
presets: [
['babel-preset-expo', { jsxRuntime: 'classic' }],
],
};
// → Uses React.createElement. Will be patched too.In both cases, the SDK works automatically. No configuration needed.
Network Requests
globalThis.fetch is monkey-patched. Every fetch call is tracked as a resource event with URL, method, status code, and duration. The SDK's own requests to the OpenObserve endpoint are excluded to avoid recursion.
JS Errors
React Native's ErrorUtils.setGlobalHandler is patched. Unhandled exceptions are captured as error events. Fatal errors trigger an immediate flush. The original error handler (red screen / LogBox) is preserved.
App Lifecycle
AppState.addEventListener('change') is used. Foreground transitions emit an application_start action. Background transitions flush all buffered events.
Configuration Reference
OpenObserveRum.init({
// ── Required ──────────────────────────────────────────────
clientToken: string, // OpenObserve API key
applicationId: string, // Application ID in OpenObserve
site: string, // OpenObserve host (no protocol)
service: string, // Service name
// ── Optional ──────────────────────────────────────────────
env: string, // default: 'production'
version: string, // default: '0.0.0'
organizationIdentifier: string, // default: 'default'
insecureHTTP: boolean, // default: false
apiVersion: string, // default: 'v1'
sessionSampleRate: number, // 0–100, default: 100
trackResources: boolean, // default: true
trackErrors: boolean, // default: true
trackUserInteractions: boolean, // default: true
trackAppState: boolean, // default: true
maxBufferSize: number, // default: 50
flushIntervalMs: number, // default: 5000
debug: boolean, // default: false
// ── Advanced ──────────────────────────────────────────────
beforeSend: (event) => event | null, // modify or discard events
});API Reference
| Method | Description |
|---|---|
| OpenObserveRum.init(config) | Initialize the SDK (call once) |
| OpenObserveRum.trackView(name, context?) | Track a navigation / screen change |
| OpenObserveRum.setUser(id, name?) | Set current user identity |
| OpenObserveRum.setGlobalContext(ctx) | Merge key-value pairs into global context |
| OpenObserveRum.addAction(type, name, context?) | Manually track a custom action |
| OpenObserveRum.addError(error, source?, context?) | Manually track an error |
| OpenObserveRum.flush() | Force flush buffered events |
| OpenObserveRum.stop() | Stop the SDK and flush remaining events |
| OpenObserveRum.isInitialized() | Check if the SDK is running |
| OpenObserveRum.getSessionId() | Get the current session ID |
Compatibility
| Platform | Version | Status | |---|---|---| | React Native (bare) | >= 0.60 | Supported | | React Native (Expo Go) | SDK >= 47 | Supported | | React Native (Expo Dev Client) | SDK >= 47 | Supported | | React | >= 16.8 | Supported | | TypeScript | >= 4.0 | Full type definitions included |
Troubleshooting
Events not appearing in OpenObserve
- Enable
debug: truein the config to see detailed console logs. - Verify your
clientToken,site, andapplicationIdare correct. - Check the Metro / Expo logs for
[RUM] Patched ...messages — you should see at least one. - Ensure the import is the first import in your entry file.
Taps not being tracked
- Check that
trackUserInteractions: true(default). - Look for
[RUM] Patched react/jsx-dev-runtime.jsxDEVin the console. - If using classic JSX transform, look for
[RUM] Patched React.createElement. - The component must have an
onPressprop — purely visual components are not tracked.
beforeSend for filtering
OpenObserveRum.init({
// ...
beforeSend: (event) => {
// Drop health-check requests
if (event.type === 'resource' && (event as any).resource?.url?.includes('/health')) {
return null;
}
return event;
},
});License
Apache-2.0
