In a real app
In practice an app developer almost never writes JSI by hand; they consume it through Turbo Modules, the Expo Modules API, or Nitro. The concept still matters when reading a crash. A stack trace going through jsi::Runtime, HostObject, or facebook::jsi::Function means the problem is in the native call layer, not in your JavaScript. And a synchronous JSI call blocks the JS thread until it returns: a slow native method called from onScroll shows up immediately.
The classic pitfall is threading. A JSI object belongs to the runtime that created it; touching it from another thread (a network callback, a native handler) corrupts memory or crashes without a useful message. Well-built libraries go through CallInvoker to hop back onto the JS thread. Related case: Reanimated’s worklets run in their own JSI runtime, which is why you cannot call a JS-thread function from a worklet without scheduleOnRN (runOnJS before Reanimated 4).
What I look at: the engine actually in use (global.HermesInternal), which libraries declare C++ in their podspec or CMakeLists.txt, and whether the project mixes bridge modules running through the interop layer with JSI modules touching the same native resources.
Related terms
New Architecture
The rewrite of React Native internals (Fabric, Turbo Modules, JSI), on by default since 0.76 and the only architecture since 0.82.
Turbo Modules
Native modules in the New Architecture: a TypeScript spec, code generated by Codegen, and lazy loading through JSI.
Fabric
The New Architecture renderer: a C++ core shared by iOS and Android that computes layout synchronously.
Hermes
Meta’s JavaScript engine for React Native, the default since 0.70, which precompiles the bundle to bytecode at build time.
Nitro Modules
Margelo’s framework for writing native modules in Swift, Kotlin, or C++ exposed to JavaScript through JSI, with bindings generated from TypeScript.