Crash, ANR & Error Monitoring for React Native
Uncaught crashes, JS-thread stalls, and handled errors — captured from the same one line that starts Vexo
August 20, 2026
Jump to section
TL;DR — Shipped in vexo-analytics 1.10
- Crash & ANR reporting: uncaught JS errors become fatal crashes, and a watchdog flags JS-thread stalls — both feed a crash-free rate and error grouping. No setup.
trackError(error, { handled }): report a caught exception without crashing the app.- Console capture:
console.log/info/warn/errorshow up alongside the session. - Get it:
npm i vexo-analytics@1.10.1— works with Expo.
If you ship a React Native app, you already know the gap: your analytics tell you what users did, and your crash reporter — a second SDK, a second dashboard — tells you that something broke. Stitching the two together at 2am is its own job. As of vexo-analytics 1.10, Vexo closes that gap. The same vexo('YOUR_API_KEY') call that already captures screens, taps, and session replay now also captures crashes, JS-thread stalls, handled exceptions, and console logs — with nothing else to wire up.
This post covers what actually shipped and how to use it. Everything below is live in 1.10.1, the current release.
Crash & ANR reporting: automatic, zero-config
On vexo() init, the SDK installs a global handler for uncaught JavaScript errors. When one escapes your app's error boundaries, Vexo records it as a fatal crash — the error name, message, and JS stack, plus the screen the user was on and the session it happened in. You don't register anything; the handler is in place from the first line.
Alongside it runs an ANR-style watchdog. In React Native your UI is only as responsive as the JS thread: a synchronous loop, a heavy JSON parse, or a blocking bridge call can wedge it for seconds, and to the user the app is simply frozen. The watchdog measures whether the JS thread keeps servicing work; when it stalls past the threshold, Vexo reports it — the same way a native Android ANR would flag a blocked main thread, but for the JS thread that actually drives a React Native UI. These are the freezes that never throw an exception, so a plain crash handler never sees them.
Both feed two things: a crash-free rate — the share of sessions that finished without a fatal crash, the single number that tells you if a release is healthy — and error grouping, which collapses the same failure across thousands of devices into one issue with a count, instead of thousands of identical lines.
Because crashes land in the same place as your analytics and session replays, the context is already there. A crash-free dip on your latest build points at the release; the grouped issue tells you which error and how many devices; the session shows you what the user did on the way in. No exporting stack traces from one tool to correlate against sessions in another.
trackError: report handled exceptions
Not every failure should take down the app. A failed network request, a rejected payment, a parse error you already catch and recover from — you want visibility into those without them counting as crashes. That's trackError:
import { trackError } from 'vexo-analytics';
try {
await syncPurchases();
} catch (err) {
trackError(err); // handled by default — not a crash
showRetryToast();
}The signature is trackError(error: unknown, options?: { handled?: boolean }). By default the error is treated as handled: it's recorded and grouped like any other error, but it does not count against your crash-free rate — because your app kept running. When something is more serious than you can recover from and you want it to move the crash-free number, opt in:
trackError(err, { handled: false }); // counts against the crash-free rateEvery error event — automatic crash or manual trackError — carries handled, isFatal, and errorName, so you can filter fatal crashes from handled noise and slice by error type in the dashboard.
Console capture: your logs, on the session
A stack trace tells you where the app died. It rarely tells you why. The breadcrumbs you alreadyconsole.log during development — the request that returned a weird shape, the state right before the branch that threw — are usually the real story, and in production they normally vanish into the device.
As of 1.10, Vexo captures them. console.log, console.info, console.warn, and console.error are recorded and shown alongside the session, so when you open a crashed or errored session you see the log lines that led up to it in order — no reproduction, no guessing at what state the app was in. It's on automatically; your existing console calls are the instrumentation. (Console output still prints to Metro / the device as usual — capture is additive.)
Getting it
Crash, ANR, and console capture need no code beyond the init you likely already have. Install the current release:
npm i vexo-analytics@1.10.1Then, in your app's entry point:
import { vexo } from 'vexo-analytics';
vexo('YOUR_API_KEY');That single call turns on crash & ANR reporting, the crash-free rate, error grouping, and console capture. Reach for trackError only where you want to report a caught exception explicitly. It works with Expo — managed or bare — the same as any React Native app; see the integration docs for the full setup, or the app health platform overview for how errors sit next to retention and analytics in the dashboard.
Frequently Asked Questions
Do I need to configure anything to get crash and ANR reporting?
No. Both the uncaught-error handler and the JS-thread-stall watchdog install when vexo() runs. There is no separate init, DSN, or config flag.
What's the difference between a crash and a handled error?
A crash is an uncaught error that escaped your app (or a trackError(err, { handled: false }) you flagged as fatal) — it counts against your crash-free rate. A handled error is one you caught and reported with trackError(err); it's recorded and grouped but does not affect the crash-free rate, since the app kept running.
Does the ANR watchdog track the native main thread?
It watches the JavaScript thread — the one that renders your React Native UI. When it stalls past the threshold, the app is frozen from the user's point of view, and Vexo reports it. It's ANR-style monitoring aimed at where RN apps actually block.
Will capturing console output slow my app or hide my logs?
No. Capture is additive — your console calls still print to Metro and the device as usual; Vexo also records log/info/warn/error so they appear alongside the session.
Does this work with Expo?
Yes — managed or bare. Install vexo-analytics@1.10.1 and call vexo(); nothing Expo-specific is required.
Questions or feedback? Reach out at hello@vexo.co or join our Discord.