How do you resolve severe performance bottlenecks in a complex React Native application caused by heavy bridge traffic, and how do you evaluate a migration to the JavaScript Interface (JSI)?
Assess the candidate's understanding of the React Native architecture, bridge performance limitations, profiling techniques, and modern solutions like JSI and the New Architecture. Use this MOBILE answer to show the decision, trade-off, and evidence rather than a memorised definition. It also connects cross platform to the point an interviewer is testing.
What the interviewer is scoring
- Whether they can diagnose and profile bridge congestion using appropriate tooling.
- Does the candidate understand the serialisation and deserialisation costs associated with the legacy bridge?
- That they can strategise a migration to JSI to enable synchronous C++ bindings.
- Whether the candidate evaluates the impact of the New Architecture (Fabric and TurboModules) on rendering performance.
- Whether they address memory management and garbage collection nuances when using JSI.
Answer
Short answer
React Native bridge bottlenecks come from serializing too much data between JavaScript and native code. Profile the bridge traffic, batch or eliminate noisy calls, move hot paths native-side, and migrate performance-critical modules to JSI or the New Architecture deliberately.
Why JavaScript-side tuning cannot fix a native bottleneck
High-traffic React Native applications relying heavily on complex animations, real-time data streaming, and frequent communication with native modules inevitably hit a performance wall. The legacy React Native bridge is the primary culprit. The naive approach to resolving UI freezing and dropped frames is to aggressively optimize JavaScript render cycles using React.memo or useCallback. While helpful, these React-land optimisations do nothing to solve the underlying architectural bottleneck: the asynchronous, batched communication model of the bridge, which requires large, deeply nested JSON objects to be continuously serialised and deserialised between the JavaScript and native realms.
Profiling and tactical mitigation
Diagnosing bridge congestion requires dedicated tooling. React Native Performance Monitor, Flipper, and native profilers like Xcode Instruments and Android Studio Profiler reveal the true cost of serialization. When the JavaScript thread is blocked by heavy serialization tasks, critical UI updates are delayed, resulting in jank.
Tactical optimisations can provide immediate relief. Native modules must be audited to ensure only essential data is transmitted across the bridge. Batching multiple small bridge calls into single, larger payloads reduces the serialization overhead. Furthermore, heavy computational tasks—such as image processing and complex string manipulation—should be shifted entirely to the native side, exposing only simple trigger methods to the JavaScript thread. For animations, strict enforcement of the useNativeDriver flag in the Animated API, or migrating to Reanimated (which operates independently of the legacy bridge), is mandatory to offload transitions to the native UI thread.
The JSI paradigm shift
Tactical improvements merely delay the inevitable; the fundamental architecture of the legacy bridge remains a limiting factor for real-time features. Migrating performance-critical modules to the JavaScript Interface (JSI) is the definitive solution. JSI allows JavaScript to hold references to C++ host objects and invoke native methods synchronously, completely bypassing the serialization overhead of the bridge.
Rewriting a real-time data streaming module using JSI involves creating a C++ layer that interfaces directly with the JavaScript engine (Hermes). Synchronous methods allow JavaScript to read chunks of binary data directly from memory, resulting in a dramatic reduction in latency and CPU usage. However, this power introduces significant complexity. Because C++ lacks JavaScript's garbage collection, memory must be meticulously managed. Robust destructors are required, and cyclic references between the JS and C++ realms must be avoided to prevent catastrophic memory leaks.
Embracing the New Architecture
Expanding the use of JSI aligns directly with the broader React Native New Architecture. Migrating to TurboModules leverages JSI to enable lazy loading of native modules, substantially reducing application startup time. Concurrently, adopting Fabric, the concurrent rendering system, further optimizes the UI thread. This requires a phased rollout plan, as third-party libraries incompatible with the New Architecture must be identified, replaced, or patched.
To prevent regressions, strict performance budgets and automated profiling must be integrated into the CI/CD pipeline. Integration tests that assert the maximum allowable bridge traffic for specific user flows are necessary to ensure future feature development does not degrade the hard-won performance gains.
flowchart TD
A["JavaScript Engine (Hermes)"] -->|Asynchronous JSON| B["Legacy Bridge"]
B --> C["Native Modules (Objective-C/Java)"]
A -->|Synchronous Calls| D["JavaScript Interface (JSI)"]
D --> E["C++ Host Objects"]
E -->|Direct Memory Access| C
D --> F["TurboModules"]
D --> G["Fabric Renderer"]Optimising React Native performance requires a deep understanding of the JS-Native boundary. Transitioning from the asynchronous, serialisation-heavy legacy bridge to the synchronous, memory-efficient JavaScript Interface (JSI) unlocks near-native performance for complex, real-time mobile applications.
© 2026 Preptima. Originally published at preptima.com.
Likely follow-ups
- How do you prioritise which native modules to migrate to JSI first when you cannot rewrite everything at once?
- What regressions would you watch for immediately after enabling Fabric on a codebase with many third-party UI libraries?
- How do you debug a memory leak introduced by a cyclic reference between a JS object and a C++ host object?
Related questions
- How do you architect and implement Dynamic Feature Modules in an Android application to reduce initial download size and deliver features on demand?hardAlso on mobile and architecture3 min
- How do you design the dependency graph, handle shared resources, and optimise build times when breaking down a massive iOS monolithic application into a modular architecture using Tuist?hardAlso on mobile and architecture3 min
- How do you leverage WorkManager to guarantee execution, handle conflict resolution, and optimise battery usage in an offline-first Android application requiring bidirectional synchronisation?hardAlso on mobile and architecture3 min
- Mobile CI/CD build times have degraded to over 45 minutes. How would you diagnose the bottlenecks and architect advanced caching, modular parallelisation, and test sharding to reduce the feedback loop by 50%?hardAlso on mobile and performance2 min