Skip to main content
Mixpanel’s React Native Session Replay SDK enables you to capture and analyze user interactions in your mobile applications. Built as a Turbo Module for React Native’s New Architecture, it provides native implementations for both iOS and Android with a unified JavaScript API.

Features

  • 📱 Cross-Platform Support - Unified API for iOS and Android
  • 🎥 Session Recording - Capture user interactions and screen recordings
  • 🔒 Privacy-First - Built-in data masking for sensitive information
  • ⚡ High Performance - Native implementation with minimal JavaScript bridge overhead
  • 🎯 Selective Recording - Configurable sampling rates and recording controls
  • 🚀 New Architecture Ready - Built as a Turbo Module with full type safety
  • 🛡️ Granular Privacy Controls - Auto-masking and manual masking via wrapper components

Requirements

  • React Native >= 0.70
  • iOS >= 13.0
  • Android API Level >= 21
  • New Architecture support (backward compatible with old architecture)

Installation

or

Platform Setup

iOS

The SDK dependencies are automatically added via CocoaPods. Your project must target iOS 13 or later.

Android

Dependencies are automatically added through Gradle. Requirements:
  • Minimum Android SDK 21+
  • Kotlin support enabled

Quick Start

Here’s a minimal example to get started with Session Replay:

Data Residency

Available in React Native Session Replay SDK version 1.3.0 and later.
If your Mixpanel project lives in the EU or India data center, or you route all Mixpanel traffic through a self-hosted proxy, set serverURL on MPSessionReplayConfig. The SDK exposes a MPDataResidency constant set with the managed region URLs so you don’t have to hardcode them. Example Usage
You can also pass any fully-qualified HTTPS URL — useful when routing replay traffic through a self-hosted proxy:
Learn more about EU Data Residency and India Data Residency.

Configuration

The MPSessionReplayConfig class provides comprehensive control over session replay behavior:

Configuration Options

Auto-Masked View Types

The MPSessionReplayMask enum defines view types that can be automatically masked:
Example - Custom Auto-Masking:

API Reference

initialize

Initialize the Session Replay SDK with your configuration.
Parameters:
  • token (required) - Your Mixpanel project token
  • distinctId (required) - User identifier for the session
  • config (required) - Session replay configuration
Validation:
  • Token must be a non-empty string
  • distinctId must be a non-empty string
  • recordingSessionsPercent must be between 0 and 100
Example:

startRecording

Start recording user interactions.
Example:

stopRecording

Stop recording user interactions.
Example:

isRecording

Check if session recording is currently active.
Returns: Boolean indicating recording status Example:

identify

Update the user identifier for the current recording session.
Parameters:
  • distinctId (required) - New user identifier
Example:

Remote Configuration

Available in React Native Session Replay SDK version 1.2.0 and later. Requires a paid Session Replay add-on.
You can set the remote_settings_mode for your project in Mixpanel under Settings > Organization Settings > Session Replay. Using this, you can quickly set SDK options remotely without needing to update your app. This allows you to adjust recording settings, such as sampling rates, on the fly based on your needs. Three modes are available:
  • Disabled: Do not use remote configuration and proceed to use the hardcoded initial options provided by the user during initialization. This is the default behavior.
  • Fallback: Attempt to retrieve remote configuration and proceed with those settings. If there is failure or timeout (500 ms), we will use the previously cached remote settings (if one exists from the last successful fetch). If no existing remote settings are cached, we will use the values from the SDK initialization config.
  • Strict: Requires successful remote configuration fetch for SDK initialization. If there is failure or timeout (500 ms), SDK initialization will fail and the Session Replay features will be unavailable for that app launch.
You can use this setting to quickly update and adjust configurations to your liking. List of currently supported remote settings:
  • recordingSessionsPercent
Settings not yet supported by remote configuration will use the value provided during initialization, or the default value if none was provided

Privacy & Data Masking

Session Replay provides two approaches to protect sensitive data: automatic masking and manual masking.

Automatic Masking

Configure which view types are automatically masked during initialization:
Default Behavior: All view types are masked by default for maximum privacy.

Manual Masking with MPSessionReplayView

Use the MPSessionReplayView wrapper component for granular control over what gets masked:

Complete Masking Example

Wireframes (Beta)

Beta. Wireframes are in beta. Before shipping to production, inspect the wireframes your app produces with debugOptions.wireframeEmitter and confirm that no sensitive information is captured.
Wireframes add AI intelligence to our Mobile Session Replay product by gathering and sending text-based representations of the user’s screen, which we feed into the agent context.

Enable wireframe capture

Set wireframesOptions on MPSessionReplayConfig.
MPWireframesOptions takes two options:

How masking applies to wireframe text

Wireframe text passes through four layers, in order. Each layer only sees what survived the one before it. An element is never removed by masking — its role and bounds always ship, so the shape of the screen is preserved. Only its text is affected. The short version: the SDK does its best to keep the wireframe consistent with what the replay video shows, plus two escape hatches you control — one to add a safe description back (Layer 3), one to catch sensitive content by pattern (Layer 4).

Declare text for an element

wireframeText lets you supply the text for an element yourself — useful for custom components, canvas-drawn content, and masked screens you still want named. Use it to say what a screen is for"Checkout summary", "Card number" — without revealing what is on it.
The wireframeText prop is independent of sensitive, which controls pixels. Set both when you want an element masked in the video but named in the wireframe. Declared text is sent verbatim, even on a masked element, so never interpolate sensitive user data into it.

Redact by pattern

Some text is sensitive because of what it contains rather than which view it came from — an account number inside a custom view the SDK has no way to recognize, for example. sensitiveRules match on the text itself and run last, after every other masking layer. There are two behaviors — redact and strip — each available as a literal or a regex match. Redact allows you to replace sensitive text; strip omits all text from the element.
Only the i, m, and s regex flags carry over to native; others are ignored with a warning. A pattern the native platform cannot compile causes initialize to reject rather than silently redacting nothing. Treat content rules as a backstop, not a boundary. A regex only catches what it matches, on the text the SDK already read. View-level masking is the stronger control; reach for rules when there is no view to mask.

Verify what you are sending

debugOptions.wireframeEmitter hands you each wireframe as it is captured, with a maskDecision per element explaining why its text was kept, rewritten, or dropped.
A few things worth knowing:
  • The emitter observes; it does not enable. Setting it without wireframesOptions captures nothing.
  • Use it in debug builds only. The emitter runs wherever you configure it, so leave it out of the release builds you ship.
  • The snapshot shape is not a stable contract. It is for interactive debugging.

Risks to mitigate

Layers 1 and 2 remove anything the video masked. They do not cover every way content can be invisible to the person looking at the screen. These are the specific gaps to check for in your own app before you enable wireframes in production.
Layer 2 strips text under regions the screenshot masked, not under an ordinary opaque view you placed on top. So when a splash view, a bottom sheet, or a loading scrim covers content, the wireframe may describe text the screenshot does not show.Usually that is fine — content you never marked sensitive is content you were happy to capture either way. It only matters if something sensitive is sitting behind the cover, in which case mask it or give it a wireframeText.
An element with no text of its own can fall back to its accessibility label (accessibilityLabel / accessible props, resolved by the native SDKs). That is what lets an icon-only button ship as button:Search instead of an anonymous shell.The risk is that a label is not drawn on screen, so — unlike visible text — you cannot confirm what it holds by watching the replay. An avatar whose label is the user’s full name ships that name with no visual cue that there was anything there to mask.Because of that, useAccessibilityLabelFallback is off by default. Turn it on only after auditing what your labels contain, and verify with the debug emitter — label-derived text is reported like any other text, with its own maskDecision.
The SDK reads the string a view was handed, which is not always the string a user can see on-screen. Ellipsized labels, lines clipped by their container, and values scrolled out of frame all report their full value. Element text is capped at 50 characters, which bounds but does not eliminate this.Mitigate: mask or declare text for anything whose full value is more sensitive than its rendered form.
wireframeText is exempt from Layers 1 and 2 — that is the whole point of it. Content rules still run over it, but nothing else does.Mitigate: treat it as a static string you wrote at build time. Never interpolate sensitive user data into it.
Automatic masking works by native view type. Custom components that draw their own content are not text components, so autoMaskedViews does not cover them — the same as for screenshots today, but now they can also carry text.Mitigate: mark custom components sensitive explicitly, and confirm coverage with the debug emitter rather than assuming.
On the legacy (Paper) architecture on iOS, a plain <Text> publishes its string only through an accessibility label, so it needs useAccessibilityLabelFallback — or wireframeText — to carry text in the wireframe. Roles and bounds are unaffected either way.That means turning the fallback on to get readable text also opts every other label-derived string in, so audit them together. Fabric, the default since React Native 0.76, reads text exactly and needs neither. Android is unaffected on both architectures.