Mobile Performance Monitoring and Session-Replay Controls for React Native

Zero-config PERF events for app start, frames, and screen renders — plus on-device switches to turn replay or perf capture off

August 20, 2026

Vexo TeamCo-Founder
Jump to section

Vexo 1.10 adds mobile performance monitoring to the same single-line SDK you already use for analytics and session replay. Once vexo() runs, the SDK measures how fast your app starts, how many frames it drops, and how long each screen takes to settle after navigation — and emits them as PERF events with no instrumentation on your side. The same release also gives you two initialization switches, sessionReplay and performance, so you can turn either capture path off on a per-launch basis. Install it with:

npm i vexo-analytics@1.10.1

It's Expo-compatible (use a Development Build, not Expo Go) and works on both the New and old React Native architectures. Nothing below requires a config plugin or a native change beyond installing the package.

Performance capture, with zero setup

There is no startTrace(), no manual timers, and no markers to place. The SDK installs a lightweight monitor when vexo() initializes and reports four metrics, each tagged with the current route so you can see which screen a number belongs to:

PERF metricWhat it measuresPayload
app_start_coldTime from the SDK loading to the app being ready on a cold launchvalue (ms), route
app_start_warmTime to become ready again after returning from the backgroundvalue (ms), route
framesSlow (>16 ms) and frozen (>700 ms) frame counts over a windowslowFrames, frozenFrames, totalFrames, route
screen_renderHow long a screen takes to settle after you navigate to itvalue (ms), route

App start is split into cold and warm so a slow first paint doesn't get averaged together with fast background resumes. Frames uses the same thresholds React Native engineers already reason about — a frame over 16 ms misses the 60 fps budget and counts as slow, and a frame over 700 ms counts as frozen (a visible hitch). The counters are flushed per screen — on every navigation and on a periodic interval — so a spike in frozenFrameslands on the exact route that produced it. Screen render measures the time for a newly-pushed screen to settle its first frames, which is the number to watch when a particular screen feels heavy to open.

One thing worth calling out: performance monitoring only runs in production builds. In development (__DEV__) the monitor installs nothing and emits nothing, because dev-mode timings are dominated by Metro, the bundler, and unminified code — they'd be noise. If you wrap initialization in if (!__DEV__) { vexo(…) } as we recommend, that lines up exactly: you'll see PERF events from real installs, not from your simulator.

The two init switches: sessionReplay and performance

vexo() takes an optional second argument. Its shape is small and boolean:

import { vexo } from 'vexo-analytics';

// Everything on (the default) — analytics, replay, and performance.
vexo('YOUR_API_KEY');

// Analytics + performance, but no screen recording.
vexo('YOUR_API_KEY', { sessionReplay: false });

// Analytics + replay, but no performance capture.
vexo('YOUR_API_KEY', { performance: false });

// Analytics only.
vexo('YOUR_API_KEY', { sessionReplay: false, performance: false });

The two flags are independent, and each is { sessionReplay?: boolean; performance?: boolean } on the VexoOptions type. Turning one off has no effect on the other, and turning either off leaves the rest of the SDK — screen and tap analytics, navigation tracking, customEvent(), and crash/ANR reporting — fully working.

The important detail for anyone shipping a performance-sensitive app: both decisions are made client-side and synchronously, on the device, before any native work begins. There is no network round-trip to decide whether to record — passing sessionReplay: false means the native recorder (and the privacy-blur pass that goes with it) is never started at all, not started and then told to stop. Server-side settings can further restrict recording, but they can never re-enable it once your code passed false. The choice also isn't persisted: each vexo() call decides for that session, so omitting the option (or passing true) keeps the default behavior on the next launch.

Turning replay off for jank-sensitive screens

Session replay records the screen natively. On the React Native New Architecture, that recording can add work to the main thread, and in a few app categories — think 60/120 fps games, camera and video capture, or heavy real-time canvases — that's exactly where you can least afford it. If you're chasing dropped frames and you suspect the recorder, sessionReplay: false is the switch to reach for:

// Perf-sensitive app: keep analytics + PERF events, drop the recorder.
vexo('YOUR_API_KEY', { sessionReplay: false });

Because the decision happens on-device before the first native call, no recorder or blur work is ever scheduled — so this is a genuine fix for main-thread jank, not a mitigation. You keep app-start, frame, and screen-render telemetry (and everything else Vexo captures) while removing the recording path entirely. If you only need replay gone on specific builds or user segments, gate the flag with your own condition at initialization — it's an ordinary boolean you compute however you like.

The mirror image is also useful: if replay is the signal you care about and you'd rather not run the frame sampler, pass performance: false to install nothing on the performance side while keeping recording on.

Expo and React Native support

Installation is one command and works the same for Expo and bare React Native:

# Expo (Development Build — not Expo Go)
npx expo install vexo-analytics@1.10.1

# Bare React Native
npm i vexo-analytics@1.10.1

The package ships native code, so it runs in an Expo Development Build but not in the Expo Go client. It supports the New Architecture (via a TurboModule) with the old architecture still working through interop, and every release is compiled against a floor-and-latest React Native matrix on iOS and Android. Screen-scoped metrics rely on React Navigation or Expo Router, which Vexo auto-attaches to — the same integration that powers screen analytics.

Frequently Asked Questions

Do I need to instrument my code to get performance data?

No. App-start time, slow/frozen frame counts, and per-screen render timing are captured automatically once vexo() runs — there are no traces, timers, or markers to add. They arrive as PERF events tagged with the screen route.

Why don't I see any PERF events in development?

Performance monitoring only installs in production builds. In __DEV__ it deliberately emits nothing, because timings under Metro and unminified code aren't representative. Run a release build (or a real install) to see PERF events.

Does sessionReplay: false also turn off analytics?

No. It only stops screen recording. Screen and tap analytics, navigation tracking, customEvent(), crash/ANR reporting, and — unless you also pass performance: false — performance capture all keep working. The two switches are independent.

Is turning replay off a real performance fix or just a toggle?

It's a real fix. The decision is made client-side and synchronously on the device before any native call, so with sessionReplay: false the native recorder and its privacy-blur pass are never started — there is no recording work to add jank to the main thread in the first place.

Ready to see it on your own app? Start free, run npm i vexo-analytics@1.10.1, and check the pricing page when you're ready to grow.

Start today for free

Our free tier is the perfect starting point to try vexo. You can upgrade at any time!