@middleware.io/middleware-react-native
v2.3.2
Published
Middleware React Native real user monitoring SDK
Downloads
744
Readme
Middleware React Native SDK
Middleware React Native Real User Monitoring SDK
Features
- AutoInstrumentation HTTP Monitoring
- AutoInstrumentaion JS Errors
- AutoInstrumenation navigation tracking for
react-navigation - AutoInstrumenation native crash errors
- Custom Instrumenation using OpenTelemetry
- Custom logging
- RUM Session Tracking
- Session Recording
Documentation
Compatibility & Requirements
Middleware React Native for Mobile supports React Native 0.68 and higher.
Since v2.0.0 the SDK wraps the stable Middleware native SDKs
(io.github.middleware-labs:android-sdk 3.1.4+ and the MiddlewareRum
CocoaPod 2.2.3+), which brings v3 session recording (rrweb replay), native
crash/ANR reporting, and screen-name linked replays. Native toolchain
requirements:
Android:
compileSdkVersion35+, Kotlin Gradle Plugin 2.0.21+ (pinclasspath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21")on React Native < 0.77),minSdkVersion21. If your app still enables Jetifier, addandroid.jetifier.ignorelist=jackson-coretogradle.properties.iOS: deployment target 13.0+, CocoaPods (the
MiddlewareRumpod is pulled automatically). When building pods as static libraries (the default), addpod 'Reachability', :modular_headers => trueto your Podfile. Also add these sources to the top ofios/Podfile:source 'https://github.com/middleware-labs/cocoapods-specs.git' source 'https://cdn.cocoapods.org/'CocoaPods trunk stops accepting new pods on December 2, 2026, so new
MiddlewareRumversions are published to the Middleware repo. Without that source, your app stays on the last version published to trunk.
The library is also compatible with the following frameworks and libraries:
- Expo (SDK 52+, via a development build — see below)
- React Navigation 5, 6 and 7
Installation
yarn add @middleware.io/middleware-react-nativeExpo
This SDK contains custom native code, so it cannot run in Expo Go — Expo Go ships a fixed native binary and has no way to load the Middleware native SDKs. You need a development build.
Add the config plugin to app.json / app.config.js:
{
"expo": {
"plugins": ["@middleware.io/middleware-react-native"]
}
}Then create a development build:
npx expo prebuild --clean # regenerate ios/ and android/ with the plugin applied
npx expo run:android # or: npx expo run:iosFor EAS, eas build --profile development picks the plugin up automatically.
The plugin applies the native requirements that a managed project can't set on its own:
| Change | Why |
| --- | --- |
| android.jetifier.ignorelist=jackson-core | Jetifier fails on the jackson-core inside android-sdk 3.x |
| Kotlin Gradle Plugin >= 2.0.21 | android-sdk 3.x is compiled with Kotlin 2.0.21 metadata |
| compileSdkVersion >= 35 | required by android-sdk 3.x (an already-higher value is left alone) |
| iOS deployment target >= 13.0 | minimum for the MiddlewareRum pod |
| pod 'Reachability', :modular_headers => true | Reachability ships no modulemap, so Swift can't import it under static libraries (skipped when the app uses use_frameworks!) |
Options, if you need to override a default:
{
"expo": {
"plugins": [
["@middleware.io/middleware-react-native", {
"kotlinVersion": "2.0.21",
"compileSdkVersion": 35,
"iosDeploymentTarget": "13.0",
"jetifierIgnorelist": true,
"reachabilityModularHeaders": true
}]
]
}
}Checking that the native SDK is actually linked
If spans stop flowing, the first thing to check is whether the native module made it into the binary:
import { MiddlewareRum } from '@middleware.io/middleware-react-native';
console.log('Middleware native linked:', MiddlewareRum.isNativeAvailable());false means you are on Expo Go, or running a binary built before the package
was added — rebuild with npx expo run:android / run:ios.
The SDK falls back to sending traces from JS over OTLP/HTTP in two cases: the native module isn't linked, or native initialization failed. Either way traces still reach Middleware, while crash/ANR reporting and session recording stay off until the native side is healthy. Native failures are logged with their cause, for example:
ERROR [MiddlewareRum] native initialize failed: Method addObserver must be called on the main threadSet debug: true in your configuration to also see per-export detail.
Usage
import { MiddlewareWrapper, type ReactNativeConfiguration } from '@middleware.io/middleware-react-native';
const MiddlewareConfig: ReactNativeConfiguration = {
serviceName: 'Mobile-SDK-ReactNative',
projectName: '$Mobile-SDK-ReactNative',
accountKey: '<middleware-account-key>',
target: '<target-url>',
deploymentEnvironment: 'PROD',
globalAttributes: {
name: '<your-name>',
},
};
export default function App() {
return (
<MiddlewareWrapper configuration={MiddlewareConfig}>
// Application Components
</MiddlewareWrapper>
);
}Custom logging
You can add custom logs such as debug, error, warn, info these logs will be shown on Middleware Logs Dashboard
MiddlewareRum.debug("I am debug");
MiddlewareRum.error("I am error");
MiddlewareRum.info("I am info");
MiddlewareRum.warn("I am warn");Setting Global Attributes
You can set global attributes by calling setGlobalAttributes function.
MiddlewareRum.setGlobalAttributes({
"name": "Middleware",
"app.version": "1.0.0",
"custom_key": "some value"
});Network instrumentation
To ignore capturing urls pass Array<String | RegExp> in ignoreUrls key in ReactNativeConfiguration
Example:
ignoreUrls: [/^\/api\/facts/, /^\/api\/v1\/users\/.*/],Capturing headers and response bodies
Network spans carry the method, URL, status and timings. Headers and response bodies are not captured by default — turn them on separately:
| Option | Default | Captures |
| --- | --- | --- |
| captureNetworkHeaders | false | Request and response headers, as http.request.header.<name> / http.response.header.<name> (lowercase, - → _). Also links the span to your backend trace from a Server-Timing: traceparent;desc="…" response header. |
| captureResponseBodies | false | Response bodies, as http.response.body, only when the Content-Type is application/json, *+json (e.g. application/problem+json) or text/plain. Truncated to 64 KiB. |
const MiddlewareConfig: ReactNativeConfiguration = {
...
captureNetworkHeaders: true,
captureResponseBodies: true,
};Request bodies are never captured.
This package depends on Android android-sdk 3.2.2+ and iOS
MiddlewareRum 2.3.0+. React Native's requests are traced by the JS
instrumentation only: native URLSession tracing is turned off on iOS, so each
request produces one span. As a result, requests made directly by native code
(native modules, third-party native SDKs, images loaded by <Image>) are not
traced on iOS. On Android the settings are passed to the native SDK, where they
apply to HTTP traced through its OkHttp call factory.
Changed in 2.3.0: earlier versions recorded request headers, response headers, request bodies and response bodies on every request. Set the options above to keep capturing headers or response bodies.
To leave specific headers out, list them in ignoreHeaders (case-insensitive;
a Set or an array):
ignoreHeaders: new Set(['Authorization', 'x-ignored-header']),Note: x-access-token is always masked.
To disable network instrumentation set networkInstrumentation: false
const MiddlewareConfig: ReactNativeConfiguration = {
...
networkInstrumentation: false
};Distributed Tracing
End-to-end tracing links a RUM session to the backend traces it caused, so you can open a slow screen in the session explorer and see the server spans behind it.
It works by trace-context propagation: the SDK creates a client span for each outgoing request and
injects the W3C traceparent header. Your instrumented backend continues that same trace, and
Middleware correlates the two by trace ID.
This is on by default and requires no code. Every request made through fetch or
XMLHttpRequest is traced and carries trace headers.
To keep your trace IDs off third-party APIs, narrow propagation to your own domains with
tracePropagationTargets, which takes Array<string | RegExp>:
const MiddlewareConfig: ReactNativeConfiguration = {
...
tracePropagationTargets: [/api.example.com/, /anotherapi.example.com/]
};Requests to other hosts are still timed and still appear in the session — they just travel without trace headers. An explicit empty array disables propagation entirely.
Prefer regexes: a RegExp entry is matched against the URL, but a plain string entry has to
equal the whole URL exactly, so 'api.example.com' matches nothing.
By default both W3C (traceparent) and B3 headers are sent. Use tracePropagationFormat: 'w3c'
or 'b3' to send only one.
Reporting custom errors
You can report handled errors, exceptions, and messages using the reportError function
try{
throw new Error("I am error")
} catch (err) {
MiddlewareRum.reportError(err);
}Updating location information
You can set latitude & longitde as global attributes.
MiddlewareRum.updateLocation(latitude: number, longitude: number)Enable session recording
By default session recording is enabled, to disable session recording pass sessionRecording: false configuration as follows -
const MiddlewareConfig: ReactNativeConfiguration = {
serviceName: 'Mobile-SDK-ReactNative',
projectName: '$Mobile-SDK-ReactNative',
accountKey: '<middleware-account-key>',
target: '<target-url>',
sessionRecording: false,
deploymentEnvironment: 'PROD',
globalAttributes: {
name: '<your-name>',
},
};Recording options
Tune how the recording is captured with recordingOptions:
const MiddlewareConfig: ReactNativeConfiguration = {
// ...
sessionRecording: true,
recordingOptions: {
frequency: 'standard', // 'low' (~1 fps, default) | 'standard' | 'high'
quality: 'standard', // 'low' | 'standard' (default) | 'high'
maskAllTextInputs: true, // default false (masks all text and WebViews)
maskAllImages: true, // default false (masks images and WebViews)
},
// Fraction of sessions that get recorded (0.0 - 1.0). Defaults to 1.0.
sessionSamplingRatio: 1.0,
};Starting and stopping recording at runtime
Recording can be controlled after initialization — useful when you only want to record a specific flow:
import { MiddlewareRum } from '@middleware.io/middleware-react-native';
await MiddlewareRum.startRecording(); // -> boolean: recording is running
await MiddlewareRum.stopRecording(); // -> boolean: recording was stopped
await MiddlewareRum.isRecording(); // -> booleanBoth calls are sticky: they survive session rotation and override the session sampler, so recording stays in the state you asked for until you change it again.
startRecording() also overrides sessionRecording: false, which lets you keep
recording off by default and turn it on only where you need it:
MiddlewareRum.init({ ...config, sessionRecording: false });
// later, e.g. when the user enters the checkout flow
await MiddlewareRum.startRecording();
// ...
await MiddlewareRum.stopRecording();Sanitizing views in session recording
Views will get blurred hiding sensitive information in session recording.
<MiddlewareSanitizedView>
<Component/>
</MiddlewareSanitizedView>Slow rendering detection
The native SDK reports slowRenders and frozenRenders spans. To turn them off, pass slowRenderingDetection: false. Relaunch the app for the change to apply, because a JS reload keeps the native settings.
const MiddlewareConfig: ReactNativeConfiguration = {
// ...
slowRenderingDetection: false,
};Contributing
See the contributing guide to learn how to contribute to the repository and the development workflow.
License
Apache 2.0
