Mobile integration
React Native Session Replay SDK: Add It to Your App
Add the AnyReplay React Native session replay SDK: requirements, install, init and the AnyReplay wrapper, screen tracking, on-device masking and known limits.
Updated · 5 min read
To add session replay to a React Native app, install @anyreplay/react-native, call init once at module level and wrap your app in <AnyReplay>. The SDK is pure JavaScript with no native module, so there is no pod install and no rebuild; it needs React Native 0.72 or later, React 18 or later and, to record the screen, the New Architecture.
This page is the install path. For how the recorder reads the element tree and why the replay is a wireframe rather than video, read session replay for React Native.
Why add session replay to a React Native app
Mobile bug reports are thin: "the button did nothing", with a device model if you are lucky. A recording shows which screens the person moved through, where they tapped, and where they tapped three times in the same spot. AnyReplay records the committed element tree (element types, text, position and size, image URLs) and replays it as a wireframe with real text, which keeps recordings small and lets a reviewer translate a session into their own language. Mobile sessions appear in the same list as your web sessions.
Requirements
- React Native 0.72 or later and React 18 or later.
- The New Architecture (Fabric), on by default from React Native 0.76 and Expo SDK 52. On the old architecture the recorder warns once and records taps and events, but not the screen.
- If the project lists allowed domains, add the app's bundle id under Allowed apps and pass it as
appId; an app sends noOriginheader.
How to install the React Native session replay SDK
npm install @anyreplay/react-native @react-native-async-storage/async-storage// App.tsx
import { AnyReplay, init, trackNavigation } from '@anyreplay/react-native';
init({
projectKey: 'YOUR_PROJECT_KEY',
ingestUrl: 'https://in.anyreplay.com',
// Your bundle id (iOS) or package name (Android). Required once the project lists allowed domains or apps.
// appId: 'com.example.app',
// Stop recording after this many sessions in a calendar month.
// maxSessionsPerMonth: 10000,
// Fields are masked: what people type replays as asterisks. Secure
// fields, passwords, one-time codes and card fields are masked either
// way. Uncomment the first line to record what people type, or the
// second to mask every field in a way nothing can weaken.
// maskAllInputs: false,
// maskAllTyping: true
});
export default function Root() {
return (
<AnyReplay>
<NavigationContainer ref={(ref) => { if (ref) trackNavigation(ref); }}>
<App />
</NavigationContainer>
</AnyReplay>
);
}init(options)starts the recorder. Keep it at module level, before the root component renders; a second call returns the existing recorder, so hot reload does not start another.<AnyReplay>wraps the app. It lets the recorder find the rendered tree and observe touches, then passes every touch straight through to your components.trackNavigation(ref)follows a React Navigation container and names each screen after its route.
AsyncStorage is optional. Pass it as storage and a relaunch within 30 minutes resumes the same session, and sampling stays stable for the same person across launches. Without it, ids live in memory and end with the app process.
init({ projectKey: 'YOUR_PROJECT_KEY', storage: AsyncStorage });Are screen changes recorded?
A mobile app has no URL, so screens are recorded when you report them. With React Navigation, trackNavigation does it for you and drops consecutive duplicates. With another navigator, call screen('Checkout') when the screen actually changes. Each screen change adds a marker, raises the page count and starts from a fresh snapshot. Screen names are not masked, so use Profile, not a name that contains an email. See navigation tracking.
How do I keep sensitive data out of recordings?
- What people type into a
TextInputis masked by default, as it is on the web. SetmaskAllInputs: falseto record typing, ormaskAllTyping: truefor masking no other option can weaken. - Masked whatever you configure:
secureTextEntry, any field whosetextContentType,autoComplete,keyboardType,placeholder,accessibilityLabel,testIDornativeIDmarks it as a password, one-time code or payment field, and any card number, whole or typed past its sixth digit. - Add a
testIDcontainingar-maskto mask an element and everything inside it, for example<Text testID="ar-mask-balance">. - Set
maskImages: trueto record no image URLs; images replay as masked boxes. - Pass
requireConsent: trueand callconsent(true)when the person agrees. Store the choice yourself and call it again on each launch, afterinithas resolved:consentdoes nothing until the recorder exists.
A masked element keeps its box and loses its words, on the device, before anything is sent. One limit is worth stating plainly: with maskAllInputs: false, a bare <TextInput /> with no props says nothing about itself, so it is recorded, exactly as an <input> with no attributes is on the web, and only the card-number check on the value can catch it. Give sensitive fields a textContentType, an autoComplete or an ar-mask testID, or keep the default. See masking and blocking.
Known limits
- Lists record the rows that are rendered, not the whole data set.
- Custom native components record as an empty box, because there is no readable content to capture.
- The buffer is flushed when the app goes to the background; if the OS kills the app without that, the last seconds can be lost.
- Maps and videos replay as labelled boxes.
- There is no SDK yet for native Swift or Kotlin apps. Flutter apps have their own SDK.
How do I check it is working?
- Pass
debug: truetoinitand watch the Metro log for warnings: an unmounted<AnyReplay>or a refused request (the old-architecture warning always shows). - Open the app, move between two screens, then send it to the background to flush the buffer.
- Find the session under Sessions in the dashboard. If requests are refused with 403, the bundle id is missing from Allowed apps.
Every option is in the React Native SDK reference, and the React Native install guide has the full details. Building with Expo? See session replay for Expo.
Frequently asked questions
Does the React Native SDK need a native module or a rebuild?
No. The package is pure JavaScript, so there is no pod install, no config plugin and no rebuild. It does require the New Architecture.
Does it work on the old architecture?
Partly. Elements are measured through Fabric, so on the old architecture the recorder warns once and records taps, screen names and events, but not the screen. The app is not affected.
Is the replay a video of the screen?
No. It is rebuilt from the recorded element tree as a wireframe with real text, positions and image URLs, which is why masked text can be removed on the device before sending.
Why are my requests refused with 403?
Most often the project lists allowed domains and the app's bundle id is not under Allowed apps. Add it there and pass the same value as `appId`.