RORK LABJP
TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the roundTOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Articles/Dev Tools
Dev Tools/2026-06-01Advanced

When Your Android Vitals ANR Rate Climbs: Notes on Keeping the Main Thread Free in React Native

How I traced and fixed a rising Android Vitals ANR rate in a Rork-built React Native app, plus the main-thread rules I rolled out across six apps in production.

React Native209Android43ANRPerformance23Crashlytics12

Premium Article

One morning a Google Play Console alert caught my eye. Out of the six wallpaper apps I run in parallel (such as Ukiyo-e Wallpapers), exactly one had its Android Vitals "ANR rate" climb to 0.61%, crossing Play's bad-behavior threshold of 0.47% that can affect store visibility. Crashes weren't up. The crash-free rate was holding at 99.6%. And yet users were experiencing something like "it freezes, goes black after a while, then closes on its own."

ANR (Application Not Responding) is a separate signal from crashes. For my first few years I watched crashes almost exclusively and never monitored ANRs properly. Across more than 50 million cumulative downloads since I started building apps on my own in 2014, it was only in the last two or three years that I really felt how nasty ANRs are: the feature works correctly, but the experience is broken. This article is the record of how I traced that one app, and the "keep the main thread free" design rules I then spread to all six.

ANR Is Not "Slow JS" — It Is "the UI Thread Stopped Responding"

When people think about React Native performance, many of us reach first for the weight of the JS thread. I thought that way for a long time. But Android's ANR is, strictly speaking, not about the JavaScript thread.

Android records an ANR mainly in these situations: an input event doesn't get a response within about five seconds, a BroadcastReceiver doesn't finish in time, or a Service doesn't complete within its window. In every case, what's being judged is the main thread (the UI thread). In React Native, JS runs on its own thread, but synchronous native module calls, large data crossing the bridge, native initialization at startup, and image decoding all ultimately show up as work on the main thread. When that gets jammed, you get an ANR no matter how light your JS is.

So reducing ANRs is not "make JS faster." It is "find the spots where the main thread is blocked continuously for a long time, and clear them." Shifting my view to this point was the first breakthrough.

One thing worth adding: since Android 11, ANR accounting has widened to include things like delays in Context.startForegroundService, not just broadcasts and input events. A point that's surprisingly easy to miss in a React Native app is the native initialization that third-party SDKs (ads, analytics, push) run at startup. My six apps all include AdMob, so whether the ad SDK's initialization overlaps with synchronous startup work is something I check every time. If you only suspect your own JS, you'll get lost when the cause is on the SDK side.

Pin Down the Location With Crashlytics and Android Vitals

I narrowed down where it was happening by squeezing it from two directions.

One is Google Play Console's Android Vitals. ANRs are grouped into "clusters" with stack traces. For the problem app, roughly 70% of the top cluster originated from nativeRunnable, and they all concentrated in the first few seconds after launch. That alone points to "somewhere in the startup sequence."

The other is Firebase Crashlytics. Lately, when I update an Android app, I have Claude in Chrome read the Crashlytics screen and organize the correlation between ANR cluster stack traces and the version they appeared in. Having it summarize the on-screen tables — "which release did this start growing from," "which devices and OS versions are over-represented" — lets me narrow my hypothesis before I open any code. This time it skewed toward Android 12 and up on lower-end devices, which read as "surfacing on devices where the synchronous startup work is slow."

My grandfather, a temple carpenter, used to say that "working with your hands is a kind of faith," and ANR investigation is exactly that: measuring each suspicious initialization one at a time is far more reliable than staring at logs and guessing.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
How I triaged one app crossing the 0.47% Android Vitals ANR threshold within 72 hours of release
Before/After code that moves synchronous startup work off the main thread, with a measured 0.61%→0.12% ANR drop
The New Architecture pitfall where synchronous TurboModule calls block the main thread, and the rule I use across six apps
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-07-10
Adding React Compiler to Expo Let Me Delete 41 Hand-Written memo Calls
I enabled React Compiler on Rork-generated React Native screens and measured the rerender counts with Profiler. Here is how I decided which memo and useCallback calls were safe to delete, how to find the components the compiler bailed out on, and how to catch regressions in CI.
Dev Tools2026-06-25
When an Image-Heavy Rork App Quietly Bloats Its Cache and Dies on Memory — Field Notes on Measuring and Capping
In a Rork app where images are the product, expo-image's disk cache and resident memory creep up over a session and surface as OOM crashes. Here's how I measured the bloat, where I set caps, and what I trimmed on the delivery side — with working code, in the order that actually helped.
Dev Tools2026-06-19
When Rork-Built Lists Stutter: Designing Image Caching and Prefetch
A FlatList from Rork starts stuttering once the images pile up. Here is how I restore smoothness with expo-image caching, recyclingKey, prefetch, and a move to FlashList, with the device numbers I measured.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →