WebNexus
← All posts

July 17, 2026 · João Paulo Marra

When JavaScript Isn't Enough: Moving React Native Hot Paths to C++ with Nitro Modules

React NativeC++Nitro ModulesPerformance

React Native performance advice usually comes in two flavors: "memoize harder" or "rewrite it native." After 16+ years shipping mobile products, I think both miss the practical middle path the new architecture gave us: keep the product in TypeScript, move the one hot path to C++, measure, stop.

On a recent production app, that approach — custom C++ Nitro Modules over JSI for the hottest code paths — bought roughly 40% better computational efficiency in those paths, without changing how the product team ships everything else.

The decision rule

Not everything deserves C++. Almost nothing does, actually. My rule of thumb for what leaves JavaScript:

  • It runs per frame, per keystroke, or per list item → candidate for native.
  • It parses, transforms, or diffs nontrivial data → candidate for C++.
  • Everything else stays in TypeScript, because iteration speed is also a feature.

The JS thread is a shared resource. The question is never "is this function slow?" — it's "what else can't happen while this function runs?"

A concrete case: markdown parsing for LLM UIs

If your app renders LLM output or chat, you're parsing markdown constantly — and streaming makes it worse, because each incoming chunk triggers a re-parse exactly when the UI is busiest animating new content. That contention shows up as dropped frames at the moment the app should look most alive.

That's why I built react-native-nitro-markdown:

  • parsing runs in C++, off the JS thread, exposed over JSI via Nitro Modules
  • the JS side receives a ready AST and just renders
  • per-chunk re-parses during streaming stay cheap

The library is open source — issues and PRs welcome.

Why Nitro Modules specifically

Nitro Modules give you the modern version of the native module story:

  1. 01JSI, not the bridge — calls are effectively function calls, not serialized messages, so fine-grained APIs stay viable.
  2. 02Typed codegen — the TypeScript interface and the C++ implementation can't drift apart silently.
  3. 03Swift/Kotlin/C++ freedom — you pick the language per module; for pure computation, C++ means one implementation for both platforms.

What I'd tell a team considering this

  • Measure first. Profile the JS thread under real usage. If your bottleneck is render count, fix that in React first — C++ won't save you from re-rendering a list.
  • Move the smallest thing that works. A parser, a diff, a crypto routine. Not "the business logic."
  • Keep the boundary boring. Plain data in, plain data out. The moment your native module holds UI state, you've built a second app.
  • Budget for ownership. A C++ module is a product: it needs CI, tests on both platforms, and someone who isn't afraid of it.

The new architecture didn't make React Native "fast" — it made performance a set of deliberate choices instead of a ceiling. That's a much better deal.

About the author

João Paulo Marra is a Staff/Principal React Native engineer with 16+ years shipping mobile products — specializing in legacy-to-Expo migrations, C++ Nitro Modules, and zero-to-one mobile delivery.