React Native 0.83 – 0.84 · For Advanced Engineers · April 2026
The New Architecture is no longer opt-in, experimental, or a migration checkbox. It is the only architecture. The bridge is gone, the legacy renderer is gone, and the technical debt React Native has carried since 2015 is officially retired. Here's what that means in real production code.
1. The Bridge is Dead. Long Live JSI.
For years, every React Native app shipped with an invisible bottleneck: the Bridge. Whenever JavaScript needed to talk to native code — triggering a gesture, mounting a view, calling a native module — data had to be serialized into JSON, thrown across a message queue to the native thread, deserialized on the other side, and the result sent back the same way. This round trip was asynchronous by design, and on a cheap Android device under load, it showed.
JSI (JavaScript Interface) replaces that entirely. Instead of message passing, JavaScript now holds direct C++ references to native objects. There is no serialization. There is no queue. A call across the boundary is a function call, as direct as calling any other JavaScript function.
| Old: The Bridge | New: JSI | |
|---|---|---|
| Communication | JSON serialization on every cross-thread call | Direct C++ bindings, no serialization |
| Mode | Async-only, queue-based | Synchronous calls now possible |
| Memory | Copies data across threads | JS and native layers share memory references |
| Jank source | Queue backpressure on heavy animation screens | Eliminated |
Fabric is the rendering pipeline rewritten in C++ on top of JSI. Synchronous layout reads, concurrent-mode compatible, shared core across iOS and Android.
TurboModules makes native modules lazy-loaded. Only the modules your app actually calls get initialized. Large apps see meaningful startup time reduction.
Codegen generates the C++ glue code at build time from your TypeScript spec files. The JS–native boundary is now type-checked. Runtime shape mismatches — a common source of cryptic crashes — are caught at build time instead.
In practice, JSI is what makes Reanimated 3's worklet model possible — UI-thread animations running at full 120fps without any JavaScript involvement. The same mechanism enables any library author to write synchronous native bindings using the Native Module API.
Real-world signal: If your app uses
react-native-reanimated,react-native-gesture-handler, orreact-native-mmkv, you were already depending on JSI. Those libraries have required it for years. The difference now is that the rest of your app's architecture is consistent with it — there is no bridge running in parallel.
Fabric's biggest practical change is that layout information is now readable synchronously from the JS thread when needed. Previously, reading a component's measured dimensions required a callback. With Fabric, measureInWindow and related APIs can return synchronously in contexts that need it — which matters for tooltip positioning, drag handles, and any UI that responds to its own geometry.
TurboModules changes how you should think about module initialization in large apps. Previously, all native modules were registered at startup — even if you never called them. If your app had 30 native modules, all 30 initialized on launch. Now, they initialize on first call. If your onboarding flow never touches the camera module, it doesn't pay the cost of initializing it.
// Before TurboModules: module available immediately at app start
// even if you never use it on this screen
import { NativeModules } from 'react-native'
const { CameraModule } = NativeModules
// After TurboModules: initialized lazily on first access
// Import the typed spec instead
import NativeCameraModule from './NativeCameraModule'
// Codegen generates the type-safe binding from your .ts spec file
// No manual type casting, no runtime shape errors
2. Hermes V1: What Actually Changed in the Engine
Hermes has been the default JS engine since 0.70. What changes in V1 (shipping as default in 0.84) is not the fundamental approach — bytecode compilation at build time, optimized for mobile memory profiles — but a significant set of internal improvements that affect both cold start and runtime behavior.
| Metric | Improvement |
|---|---|
| Cold start | ~50% faster |
| Memory usage | ~30% lower |
| Bundle parse | Pre-compiled at build time (unchanged model, better optimizer) |
The 50% faster startup figure comes primarily from improved bytecode optimization and better handling of the concurrent rendering model. Hermes V1 is built with React 19's concurrent features in mind — it understands fiber scheduling at the engine level and schedules work accordingly, rather than treating the scheduler as an opaque JS library.
What you need to do: Nothing. If you're on 0.84 with Expo SDK 55, Hermes V1 is the default. You don't configure it. Where you will notice it: time-to-interactive in cold launch telemetry, and reduced memory pressure on low-end Android devices that previously hovered at the edge of OOM.
One behavioral change worth knowing: Hermes V1 has stricter handling of certain edge-case JavaScript patterns that the old engine tolerated silently. If you're using any libraries that relied on non-standard property enumeration order, or that mutate frozen objects and catch the TypeError, run your test suite carefully after upgrading. These cases are rare but they exist in older unmaintained libraries.
3. React 19.2: Two Features That Matter for Mobile
React Native 0.83 ships React 19.2. Most of what's in React 19.2 is web-focused, but two additions are directly useful in mobile contexts: the Activity component and useEffectEvent.
Activity
Activity is a component that controls the visibility and lifecycle of a subtree without unmounting it. It has two modes: visible and hidden. When a subtree is hidden, it is removed from the rendered output but its state is preserved. When it becomes visible again, it remounts instantly — no data fetching, no loading states, no state initialization.
import { Activity } from 'react'
function TabLayout({ activeTab }) {
return (
<>
<Activity mode={activeTab === 'feed' ? 'visible' : 'hidden'}>
<FeedScreen />
</Activity>
<Activity mode={activeTab === 'search' ? 'visible' : 'hidden'}>
<SearchScreen />
</Activity>
</>
)
}
// The hidden tab retains its scroll position, search query,
// loaded data, and all local state. Switching tabs is instant.
This replaces the common pattern of using display: none on a View to preserve state — except the Activity version is coordinated with React's scheduler and works correctly with concurrent rendering. The old display: none approach kept the component in the tree and in the layout system; Activity properly defers work on hidden subtrees.
Real-world use case: Tab navigators. Chat apps where you switch between a conversation list and an active conversation. Any screen where the user's position (scroll, form state, cursor position) should survive a navigation away and back. With
Activity, you stop managing that state manually.
useEffectEvent
useEffectEvent solves a specific and frustrating problem: effects that depend on values that change frequently, but where the dependency on those values isn't actually part of the "trigger" for the effect — it's just used inside it.
import { useEffect, useEffectEvent } from 'react'
function AnalyticsTracker({ screenName, userId, theme }) {
// This is "event" logic — it reads userId and theme
// but we don't want the effect to re-run when they change
const onVisit = useEffectEvent(() => {
analytics.track(screenName, { userId, theme })
})
useEffect(() => {
onVisit()
}, [screenName]) // only re-runs when screenName changes
// userId and theme are read fresh, not as deps
}
// Previously: you'd either add userId and theme to deps (wrong fires)
// or use a ref (suppresses the lint rule, ugly pattern)
In analytics, logging, and any effect that reads ambient state but should only fire on specific triggers, this eliminates the useRef workaround pattern that was widespread in large codebases.
4. Network and Performance Panels in React Native DevTools
Debugging performance in React Native used to mean adding manual log statements, integrating third-party profiling tools, or switching to Flipper and hoping it connected properly. The React Native Core team shipped built-in Network and Performance panels in DevTools, stabilized in 0.83.
The Network panel shows all outbound requests from your app — fetch, XMLHttpRequest, WebSocket — with timing, headers, and response bodies. It works without any library integration. You open DevTools, and your network traffic is already there.
The Performance panel integrates with performance.mark and performance.measure, which are now stable browser-compatible APIs in React Native. You can annotate your own code with named timing points and they appear in the performance timeline.
// Mark where critical work begins and ends
performance.mark('product-list:render-start')
const products = await fetchProducts(categoryId)
setProducts(products)
performance.mark('product-list:render-end')
performance.measure(
'product-list:full-cycle',
'product-list:render-start',
'product-list:render-end'
)
// Shows up in the DevTools Performance panel timeline
// correlated with React fiber work and native frame timing
Why this matters: You can now correlate your application logic timing with React's render phases and native frame drops in a single unified view. Identifying whether a slow screen is caused by a slow network call, an expensive render, or a dropped frame on the native side no longer requires three separate tools.
5. Migrating: What Actually Breaks
If your app was already on the New Architecture (which has been the default since 0.76), the upgrade from 0.82 to 0.83/0.84 is largely smooth. The 0.83 release was explicitly a stability release with no user-facing breaking changes. The real work happens if you are migrating from the legacy architecture now.
Common migration blockers:
- Old-style
NativeModulesaccess - Untyped native modules
- Libraries using direct Bridge calls
- Synchronous native module calls (wrong pattern for the bridge era)
- Frozen object mutation in third-party deps (Hermes V1 is stricter)
-
AsyncStorage— replace with MMKV or Keychain
The most common real blocker is third-party libraries that have not migrated to the New Architecture APIs. The community has largely caught up, but niche libraries with low maintenance activity may still rely on the bridge. Use the react-native-new-architecture compatibility checker before migrating and audit your native dependencies.
One change that catches teams off guard: the build flag RCT_REMOVE_LEGACY_ARCH=1 in 0.83 lets you explicitly strip the legacy arch code from your build output, reducing binary size and build time. This is experimental but production-viable for most apps.
# In your Podfile, to strip legacy arch code from the build:
RCT_REMOVE_LEGACY_ARCH=1 bundle exec pod install
# Reduces binary size, shortens build time
# Only safe after confirming no native deps use legacy APIs
6. Version Support: What the Team Is Now Committing To
React Native now maintains the latest three minor series simultaneously. When 0.84 is current, the team actively patches 0.84.x, 0.83.x, and 0.82.x. Versions below that receive no further updates.
Starting April 2026, the minimum supported version across the broader ecosystem (including third-party SDKs that explicitly track React Native's support policy) is 0.80. If you are running anything below that, you are on your own for security patches and bug fixes.
The practical implication for teams that move slowly: you now have a defined window. Three minor versions of runway before a release becomes unsupported. Given that React Native ships approximately four minor versions per year, that is roughly nine months of supported life per version. Plan upgrades accordingly — waiting for "stability" before upgrading means you will always be behind.
Recommended posture: Stay on the N-1 version (one behind latest). You get battle-tested stability, patch support, and enough time to evaluate breaking changes in N before they land in your release branch. Staying on N-2 or older is now a deliberate decision to accept unsupported software.
Closing
The through-line across all of these changes is the same: React Native is done paying off technical debt and is now shipping on top of a stable foundation. JSI, Fabric, TurboModules, Hermes V1 — these are not features you turn on. They are the floor. What gets built on top of them in the next two years is what the ecosystem is now positioned for.
Sources: React Native 0.83 release notes · React Conf 2025 · Software Mansion 2026 State of RN · Expo SDK 55 changelog
Top comments (0)