Skip to main content

Getting Started

Please refer to our Quickstart Guide. The Full API Reference, Library Source Code, and an Example Application is documented in our GitHub repo.

Installing the Library

After setting up your development environment for React Native, navigate to your app’s root directory and install the Mixpanel React Native SDK. The library requires React Native v0.6+.
Then navigate to your application’s iOS folder, and install the dependencies. (Note you do not need to update your Podfile to add Mixpanel.)
Since Xcode 12.5, there is a known swift compile issue, please refer to this workaround. However the compile issue has been resolved in Xcode 13.2.1+, there is no extra step required as long as you upgrade to Xcode 13.2.1+.
After installation, import the Mixpanel class from the SDK, create an instance of Mixpanel using your project token, then initialize by calling .init().

Library Configuration

For projects with EU or India data residency, you must configure the SDK to use the correct regional endpoint. Events sent to the wrong region will not be ingested. Learn more about Privacy-Friendly Tracking.
The Mixpanel constructor accepts up to four arguments: your project token, trackAutomaticEvents, an optional useNative flag (defaults to true), and an optional storage adapter for JavaScript mode. Additional runtime configuration is passed to init().
The fifth argument, featureFlagsOptions, configures Feature Flags. Pass { enabled: true } and optional context and persistence fields to opt in. See Feature Flags for the full configuration reference. A sixth argument, autocaptureOptions, opts in to autocaptured click events. It is omitted above because autocapture is off unless you pass it. See Click Events for the full configuration reference.

Javascript Mode

The Mixpanel React Native SDK supports React Native for Web and other platforms utilizing React Native that do not support iOS and Android directly via Javascript Mode. To enable Javascript Mode:
  1. Install AsyncStorage which is used to persist data. If this is unavailable in your target environment, you can import/define a different storage class. Please refer to this documentation.
  1. Initializing the Mixpanel object with useNative set to false.
When using Javascript Mode:
  • Legacy Automatically Tracked Events are not supported
  • Javascript Mode does not have the same default properties as Native Mode
  • Data does not automatically flush when the app is backgrounded. Be sure to call .flush() more frequently for key events

Sending Events

Use the .track() method to send an event by providing the event name and any event properties. This will trigger a request to the /track API endpoint to ingest the event into your project.
The /track endpoint will only validate events with timestamps within the last 5 days of the request. Events with timestamps older than 5 days will not be ingested. See below on best practices for historical imports.
Example Usage

Timing Events

You can track the time it took for an action to occur, such as an image upload or a comment post, using .timeEvent(). This will mark the “start” of your action, which will be timed until you finish with a track call. The time duration is then recorded in the “Duration” property. Example Usage

Flushing Events

To preserve battery life and customer bandwidth, the Mixpanel library doesn’t send the events you record immediately. Instead, it sends batches to the Mixpanel servers every 60 seconds while your application is running, as well as when the application transitions to the background. Call .flush() manually if you want to force a flush at a particular moment. Example Usage
Javascript
Flush Batch Size By default, Mixpanel will flush events immediately if a batch reaches 50 events. Use the .setFlushBatchSize() method to adjust the batch size limit for flushing. Example Usage
Javascript

Importing Historical Events

The React Native SDK is a tracking SDK designed for real-time tracking in a client-side environment. Calling track() triggers a request to our /track API endpoint, which will validate for events with a timestamp that is within the last 5 days of the request. Events older than 5 days will not be ingested. For bulk import of historical events older than 5 days, we will need to use the /import API endpoint which is optimized for scripting and supports ingesting historical data. We recommend the Python SDK (see the .import_data() function) and mixpanel-utils module (see the import_events() function) which both leverages the /import API for event ingestion.

Setting Super Properties

Super properties are global event properties that you define once and apply to all events. To register super properties, call .registerSuperProperties(). Use .registerSuperPropertiesOnce() to register super properties without overwriting existing values. Example Usage
If you have properties you’d like all events to include, you can also set the super properties when initializing the Mixpanel object by passing them as the second argument to .init(). Example Usage
Our mobile libraries store your super properties in local storage. They will persist so long as the app is installed (between launches and updates). Uninstalling the app will remove that customers super properties. See more methods related to super properties in the complete library reference here.

Managing User Identity

You can handle the identity of a user using the .identify() and .reset() methods. Learn more about identity management and identifying users.

Identify

We recommend against calling .identify() for anonymous visitors to your site.
Call .identify() when you know the identity of the current user, passing in their user ID as an argument. This is typically at account registration and at log in. Example Usage

Call Reset at Logout

Call .reset() to clear data attributed to a user when they logout. This will clear the local storage and allows you to handle multiple users on a single device. Example Usage
Javascript

Storing User Profiles

Once your users are identified, create user profiles by setting profile properties to describe them. Example profile properties include “name”, “email”, “company”, and any other demographic details about the user. The React Native SDK provides a few methods for setting profile properties under the People class accessible via .getPeople(). These methods will trigger requests to the /engage API endpoint.

Setting Profile Properties

You must call .identify() before setting profile properties in order to associate the profile properties you set with the target user. If identify is not called, the profile update will be queued for ingestion until an identify call is made.
Set profile properties on a user profile by calling the .getPeople().set() method. If a profile property already exists, it will be overwritten with the latest value provided in the method. If a profile property does not exist, it will be added to the profile. Example Usage
Javascript

Other Types of Profile Updates

There are a few other methods for setting profile properties. See a complete reference of the available methods here. A few commonly used people methods are highlighted below:
The .getPeople().setOnce() method set profile properties only if they do not exist yet. If it is setting a profile property that already exists, it will be ignored.Use this method if you want to set profile properties without the risk of overwriting existing data.Example Usage

Group Analytics

Read more about Group Analytics before proceeding. You will need to have the group key defined in your project settings first.
Mixpanel Group Analytics is a paid add-on that allows behavioral data analysis by groups (e.g. company, team), as opposed to individual users. A group is identified by the group_key and group_id.
  • group_key is the event property that connects event data to a group. (e.g. company)
  • group_id is the identifier for a specific group. (e.g. mixpanel,company_a,company_b, etc.)
The React Native SDK provides a few method for adding individual users to a group and setting group profile properties.

Adding Users to a Group

All events must have the group key as an event property in order to be attributed to a group. Without the group key, an event cannot be attributed to a group. Call the .setGroup() method to register the current user to a group, which would add the group_key as an event property set to the group_id value to all events moving forward.
Javascript
Multiple Groups An event can be attributed to multiple groups by passing in the group_key value as a list of multiple group_id values. Call .addGroup() to add additional group_ids to an existing list. Example Usage

Adding Group Identifiers to User Profiles

To connect group information to a user profile, include the group_key and group_id as a user profile property using the getPeople().set() call. Example Usage
Javascript

Setting Group Profile Properties

Create a group profiles by setting group properties, similar to a user profile. For example, you may want to describe a company group with properties such as “ARR”, “employee_count”, and “subscription”. To set group profile properties, specify the group that needs to be updated by calling .getGroup(), then set the group properties by chaining the .set() method, which will trigger a request to the /groups API endpoint. Example Usage

Other Group Profile Methods

See all of the methods under the Group class here. A few commonly used group methods are highlighted below:
The .getGroup().setOnce() method set group profile properties only if they do not exist yet. If it is setting a profile property that already exists, it will be ignored.Use this method if you want to set group profile properties without the risk of overwriting existing data.Example Usage

Feature Flags

Feature Flags let you control the rollout of features, run A/B experiments, and change application behavior without shipping new code. They work in both native mode (iOS and Android) and JavaScript mode (React Native Web). Enable feature flags by passing featureFlagsOptions with enabled: true to .init(), then evaluate flags on mixpanel.flags.
See the Feature Flags (React Native) guide for the full API, including runtime targeting, persistence policies, sync evaluation, and context updates.

Autocapture

The Mixpanel React Native SDK provides methods on the autocapture object to help you track common user interactions with minimal instrumentation. These methods fire standardized events that are recognized by the Mixpanel platform.

Screen View & Screen Leave

React Native apps use JavaScript-driven navigators like React Navigation or Expo Router, each handling screen transitions differently. This makes it impractical for the SDK to automatically detect screen boundaries. Instead, you hook trackScreenView and trackScreenLeave into your app’s navigation, giving you full control over what counts as a screen and when transitions happen. Track screen navigations using trackScreenView and trackScreenLeave. These methods fire the following events: Both events automatically include current_page_title (the screen name) and $mp_autocapture: true.
Use onReady and onStateChange on the NavigationContainer to track screen transitions:

Passing Custom Properties

Both methods accept an optional properties object. For example, forwarding UTM params from a deep link:

Click Events

Beta. Autocaptured click events are in beta: the events and the properties they capture may change before general availability. They require React Native SDK v3.7.0 or later, run in native mode only, and are disabled by default — you opt in with autocaptureOptions at initialization.
Starting in v3.7.0, the SDK can capture taps for you, with no per-component instrumentation. Capture happens in the underlying iOS and Android SDKs, which intercept touches across every window in your app — screens, modals, bottom sheets — below the React Native bridge, so no touch handler of yours is involved. Three events are produced: All three carry $mp_autocapture: true, alongside the properties described in Tracked Properties. Because capture is native, click autocapture requires native mode — the default useNative: true. In JavaScript mode (React Native Web) there is no native view hierarchy to observe: autocaptureOptions is ignored and the SDK logs a warning. Use trackClick there instead.

Click

A $mp_click event is emitted for every tap that resolves to an element, whether or not that element handles the tap. Scrolls, swipes and long presses do not produce clicks. When the tap lands on a non-interactive child — such as a <Text> inside a <Pressable> — the SDK walks up to the nearest clickable ancestor (up to 10 levels) and reports that element’s identity. This keeps a button from being reported as its label. If no clickable ancestor is found within 10 levels, the tapped element’s own identity is used. The walk-up matters more in React Native than on either platform natively. Hit-testing returns the deepest view, which for a <Pressable> wrapping a <Text> is the text — and the text carries no identity of its own. The walk-up is what puts the identity back on the pressable, so put your identifiers there, not on the text inside it. On iOS the walk-up only succeeds if you make the pressable recognisable to the platform, because React Native pressables are not clickable as far as iOS is concerned. Without accessibilityRole="button" the tap resolves to the <Text> and your id is never reported — see Why iOS needs accessibilityRole. Android needs nothing.

Dead Click

A $mp_dead_click event is emitted when a tap on an interactive element produces no visible response. The SDK snapshots the UI around the tap and compares it 500 ms later; if nothing has changed, the event is emitted. That time window is the default deadClick.timeWindowMs and is configurable. A dead click is always accompanied by the $mp_click for the same tap. Only interactive elements are candidates, so tapping a plain <Text> or a decorative <Image> never produces one. Controls with inherent visual feedback are excluded by type, because their response happens in a layer the detector cannot observe — in React Native terms, <TextInput>, <Switch>, slider components, and native pickers and date pickers.
On iOS, a <Pressable> is not “interactive” unless you say so. Add accessibilityRole="button" to any pressable you want dead click coverage on, or it will never produce $mp_dead_click — and its id will not resolve either. See Why iOS needs accessibilityRole. Android needs nothing.
Detection is based on UI change only — the SDK does not observe network activity. A handler that fires a request but leaves the screen unchanged for the dead click time window is reported as a dead click, which is usually the user-perceived truth. Conversely, a response that only changes color, opacity or an animation may not register as a change.

Rage Click

A $mp_rage_click event is emitted when 4 or more taps land within 1000 ms of each other and within 44 units on screen — dp on Android, points on iOS — the signature of a user hammering a control that isn’t responding. These three thresholds are the defaults — clickThreshold, timeWindowMs and radius on rageClick — and are configurable. The event is emitted in addition to the individual $mp_click events, so four rapid taps produce four clicks and one rage click. Taps spread out in time or across the screen do not qualify. Rage clicks are a frustration signal: alongside dead clicks, they surface the places where your app is failing users without throwing an error. Break them down by $el_id to find the specific controls people are fighting with, or use them to segment sessions worth watching in Session Replay.

One-Time Setup: Element IDs

Do this once, before you enable click autocapture. It isn’t strictly required — autocapture works without it and every tap is still captured — but it’s what makes the data readable. An element with no identifier reports a positional hash like ReactViewGroup_a424435a: it groups correctly, but it tells you nothing in a report and changes if you reorder siblings. Use the id prop. It is the one identifier that resolves the same way on both platforms: React Native copies it onto the view’s nativeID, which both SDKs read first.
  • id, not testID. testID diverges by platform: iOS maps it to accessibilityIdentifier, which the SDK reports, but on Android React Native’s view ids are generated at runtime and have no resource entry name, so the element falls through to the hash. If you already have testID values you want to keep, add a matching id alongside — the two props coexist.
  • accessibilityLabel is not an identifier. It is never used as identity on either platform, and never reported as a property. It is localized — the same element would report a different id per language — and it can carry user data.
  • Put the id on the pressable. Identity resolves from the nearest clickable ancestor, so an id on the <Text> inside a button, or on a non-clickable <View> wrapping it, is never used.
  • Add accessibilityRole="button" too. On iOS it is what makes the id resolve at all, and it is required for $mp_dead_click — see Why iOS needs accessibilityRole. Android does not need it.
Full precedence rules are in Element ID Resolution.

Enabling Click Autocapture

Pass an autocaptureOptions object as the sixth argument to init(). Passing null or omitting it leaves autocapture entirely uninitialized.
Each signal is configurable, and can be turned off independently. Every key accepts either a boolean shorthand — click: false is the same as click: { enabled: false } — or an options object; any key you omit keeps its default.
Verify on a release build. In a React Native debug build, RN’s full-screen DebuggingOverlay sits above your UI, so every tap resolves to that overlay and none of your element IDs are observable. Build in release mode to check what autocapture actually reports.
Click autocapture runs on iOS and Android only. On iOS it is not available on Mac Catalyst, tvOS, watchOS or visionOS, and does not run inside app extensions.

Disabling Click Autocapture

Autocapture is a decision made at initialization: omit autocaptureOptions and none of the machinery is constructed. To turn it off in an app that already ships with it enabled, either stop passing the options, or disable each signal:
There is no runtime toggle. Autocapture starts or doesn’t at initialization, and it respects opt-out: it does not start when tracking is opted out, and stops and restarts with optOutTracking() and optInTracking().

Tracked Properties

Every click, dead click and rage click event carries:
$el_tag_name is the native class, not your component. Capture happens below the bridge, so a <Pressable> is reported as the view React Native rendered for it — ReactViewGroup on Android, an RCTView subclass on iOS — not as Pressable. Group your reports by $el_id, which is yours to control; treat $el_tag_name as a coarse hint only.
Autocapture does not capture element text: there is no $el_text property. There is also no $attr-aria-label property. accessibilityLabel is accessibility metadata — surfaced to assistive technologies such as VoiceOver and TalkBack rather than shown on screen — and it can carry personal data, so the SDK neither reports it nor uses it as an identifier. A label auto-derived from child text is likewise never reported.

Element ID Resolution

$el_id is resolved by the native SDKs using fixed rules — it is not configurable. Understanding the order is the key to getting readable IDs in your reports. Android:
  1. nativeID — set it with React Native’s id prop
  2. Android resource entry name — React Native’s view ids are generated at runtime and have none, so testID does not resolve here
  3. <SimpleClassName>_<hash> — the anonymous fallback, e.g. ReactViewGroup_a424435a
iOS:
  1. nativeID — set it with React Native’s id prop
  2. accessibilityIdentifier — React Native maps testID to this
  3. <ClassName>_<hash> — the anonymous fallback
So id is stable on both platforms, testID is stable on iOS and a hash on Android, and accessibilityLabel is never a source on either. Which element the id is read from is a separate question from which property is read. Identity comes from the nearest clickable ancestor, and a named but non-clickable wrapper never absorbs a click that landed on its child — on either platform. On iOS that makes accessibilityRole="button" the difference between your id being reported and a hash being reported; see Why iOS needs accessibilityRole. About the hash fallback: the hash is derived from the element’s position in the hierarchy, not its instance, so it is identical across app launches — the same un-instrumented button reports ReactViewGroup_a424435a every launch. That makes it groupable, but it describes a position rather than a thing: reordering siblings changes it, and two rows of the same list differ by index rather than by content.
Review your identifiers for personal data. $el_id is derived from identifiers you set in your own code, so its contents are under your control — and Mixpanel receives them as-is. An identifier built from user data (user_4172@example.com_row, an account number, an order ID) sends that data to Mixpanel as an event property. Before you rely on click autocapture in production, tap through your key flows on a release build with SDK logging enabled, review the $el_id values the SDK reports, and confirm none of them carry personally identifiable information.

Why iOS needs accessibilityRole

Autocapture runs below the bridge, in the native SDKs, and asks the platform a simple question about the view it hit: is this thing clickable? On Android the answer is available. On iOS it isn’t — and that difference comes from React Native, not from Mixpanel. Android. React Native sets the focusable prop on Pressable and the Touchable* family, and its Android view manager turns that into a real OnClickListener. The view then reports isClickable(), which is exactly what the Android SDK reads. Nothing is required of you. iOS. React Native dispatches every touch from a single gesture recognizer mounted on the surface root, so individual views never receive one. It also does not forward focusable to iOS, and it writes accessibility traits only from the accessibilityRole / role prop. The result is that a <Pressable onPress={…}> and a plain <View> are the same object to UIKit: the same RCTViewComponentView class, no UIControl, no gesture recognizer, no accessibility trait, and canBecomeFocused == false on both. There is no signal left for the SDK to read. This is not a limitation Mixpanel can work around — the information does not exist at the layer where capture happens, and it would be equally absent to any other analytics SDK. The one way to supply it is React Native’s own API:
accessibilityRole="button" sets the underlying UIAccessibilityTraitButton, which the SDK reads as “this is a control”. It is also what VoiceOver needs to announce the element as actionable, so it is worth setting regardless. What this affects. Both halves, which is why it is worth doing once, everywhere. The tap is still captured — $mp_click fires either way — but without the role it resolves to the <Text> inside the button rather than the button, so $el_id is a positional hash instead of your id. And because iOS has no interactive element to watch, $mp_dead_click is never reported for that pressable at all. Android is unaffected throughout, so until you add the role expect the same app to report ids and dead clicks on Android that it does not report on iOS.

Manual Click Tracking

The autocapture object also exposes trackClick, trackRageClick and trackDeadClick, which emit the same three events with element metadata you supply. Use them where automatic capture can’t reach: in JavaScript mode, or for a custom gesture handler that the native hit-test resolves to the wrong element.
elementId is required and becomes $el_id; x and y become $x and $y. tagName, role and elements are optional and map to $el_tag_name, $attr-role and $elements. Each method takes an optional second argument of custom properties. $mp_autocapture: true is added for you, so these events land in the same reports as the automatic ones.
These methods also accept an accessibleLabel field, which is sent as $attr-aria-label. Automatic capture never populates it. Pass it only if you are certain the label carries no personal data.

Best Practices

  • Give every element you want to measure a stable id. <Pressable id="checkout_button">. Without one, the element reports a positional hash — groupable, but unreadable in reports, and it changes if you reorder siblings.
  • Prefer id over testID. id resolves on both platforms; testID resolves on iOS and falls through to the hash on Android. Keep both if your tests need testID.
  • Put the id on the pressable, not on a wrapper or on the text inside it. Hit-testing lands on the <Text> and identity resolves up to the nearest clickable ancestor, so an id on a non-clickable wrapper is never used.
  • Add accessibilityRole="button" to anything you want dead clicks on. On iOS a <Pressable> is indistinguishable from a plain <View> without it, so $mp_dead_click is never reported. Android is unaffected.
  • Keep ids static across renders. One built from a row index, a timestamp or a product name produces a new $el_id per render, which scatters one control across many rows in your reports.
  • Don’t reach for accessibilityLabel. It is never used as identity and never reported. Setting only a label leaves the element on the hash fallback.
  • Verify — and review for PII — on a release build. Debug builds route every tap to RN’s DebuggingOverlay. Build in release mode, call mixpanel.setLoggingEnabled(true), tap through your key flows, and confirm the reported $el_id values are the ones you expect and that none of them contain personal data. Identifiers are yours to choose, so keeping PII out of them is yours to confirm.

Session Replay

Session Replay records the mobile UI of your React Native app so you can see what your users saw. Recording ships as a separate package, @mixpanel/react-native-session-replay, and installs alongside the main mixpanel-react-native SDK.
See the Session Replay (React Native) guide for platform setup, configuration options, and privacy masking APIs.

Debug Mode

To enable debug mode, call the .setLoggingEnabled() with true, then run your iOS project with Xcode or android project with Android Studio. The logs will be available in the console. Example Usage
Remove this parameter before going into production.
Learn more about debugging.

Privacy-Friendly Tracking

You have control over the data you send to Mixpanel. The React Native SDK provide methods to help you protect user data. Learn more about Privacy.

Opt Out of Tracking

The React Native SDK is initialized with tracking enabled by default. Use the .optOutTracking() method to opt the user out of data tracking and local storage for the current Mixpanel instance. Example Usage
Opt Out by Default You can initialize the library with users opted out of tracking by default by passing optOutTrackingDefault as the first argument to .init(). Once the user is ready to be tracked, call .optInTracking() to start tracking. Example Usage

EU Data Residency

Route data to Mixpanel’s EU servers by setting the serverURL to https://api-eu.mixpanel.com. The recommended approach is to pass the serverURL to .init() so it is set before any events are sent. The .init() method accepts the serverURL as its third argument (available in SDK v3.3.0 and above). Learn more about EU Data Residency. Example Usage
Alternatively, you can call .setServerURL() to change the serverURL after initializing the client.

India Data Residency

Route data to Mixpanel’s India servers by setting the serverURL to https://api-in.mixpanel.com. The recommended approach is to pass the serverURL to .init() so it is set before any events are sent. The .init() method accepts the serverURL as its third argument (available in SDK v3.3.0 and above). Learn more about India Data Residency. Example Usage
Alternatively, you can call .setServerURL() to change the serverURL after initializing the client.

Disable Geolocation

The React Native SDK parse the request IP address to generate geolocation properties for events and profiles. To disable geolocation, call the setUseIpAddressForGeolocation() method with a value of false. Learn more about geolocation. Example Usage

Tracking Via Proxy

You can route events from Mixpanel’s SDKs via a proxy in your own domain, which can reduce the likelihood of ad-blockers impacting your tracking.
image
There are two steps: setting up a proxy server and pointing our JavaScript SDK at your server. Step 1: Set up a proxy server The simplest way is to use our sample nginx config. This config redirects any calls made to your proxy server to Mixpanel. Step 2: Point our React Native SDK at your server When initializing, replace <YOUR_PROXY_DOMAIN> with your proxy server’s domain and pass it as the third argument to .init().

Release History

See All Releases.