The New Architecture is now the default. It's also causing real issues — performance regressions, layout bugs, animation stuttering, and library crashes. Here's how to systematically find and fix them.
React Native's New Architecture — Fabric renderer, TurboModules, and JSI — replaced the old bridge-based system. As of React Native 0.82 and Expo SDK 55, you can no longer disable it. The old architecture is frozen: no features, no bugfixes.
The migration has been years in the making, and the performance improvements are real — synchronous native calls, no JSON serialization overhead, concurrent React support. But the transition has been rough. The 2025 State of React Native survey found that 54% of developers cite better debugging as their top request, and the New Architecture remains a pain point even at ~50% adoption.
The issues are real and well-documented. Shopify's migration of their two largest apps saw stability drops, ANR spikes, and 20% load time increases on complex components. Reanimated has open issues with severe animation degradation on Android. React-native-screens has layout desync bugs in Fabric. The new React Native DevTools are improving but still maturing.
If you've migrated and things are broken — or you're about to migrate and want to know what to watch for — this guide covers the four most common categories of post-migration issues and how to debug each one.
The most common surprise: your app gets slower after migration. Components that rendered in milliseconds now take hundreds. Lists stutter. Screen transitions lag.
This usually isn't the New Architecture being slower — it's the New Architecture exposing inefficiencies that the old bridge masked. The old architecture's asynchronous batching hid expensive renders behind UI thread decoupling. Fabric renders synchronously for user interactions, which means expensive components now directly block the UI.
React Native DevTools Performance panel (0.83+). React Native 0.83 introduced a Performance panel modeled after Chrome's. Record a trace, then inspect the Scheduler tracks — Blocking, Transition, Suspense, and Idle. Look for render phases that exceed 16ms (the budget for 60fps). Pay attention to the "Remaining Effects" phase: if a re-render triggers a long-running useEffect, the UI won't update until that effect finishes.
React Profiler. Use the Components tab in DevTools to profile renders. Sort by render duration to find your most expensive components. The "Why did this render?" feature tells you whether it was a props change, state update, or parent re-render.
Runtime context tools. Tools like Limelight can profile renders automatically and correlate them with the state changes and network requests that triggered them. This is especially useful for identifying cascade effects — where one slow component triggers a chain of re-renders across the tree.
React.memo. Wrap in useCallback.useMemo.getItemLayout or switch to FlashList / Legend List.babel-plugin-transform-remove-console in release builds.When you profile a performance regression, you're looking for the causal chain from trigger to slow render:
Trigger: User typed in SearchInput
State update: searchQuery → "react native new arch"
Re-renders:
SearchInput (0.4ms) ✓
SearchResults (2.1ms) ✓
DashboardStats (148ms) SLOW
↳ cause: parent re-render (not props/state)
↳ expensive: filterTransactions() called in render
ActivityFeed (89ms) SLOW
↳ cause: unstable onRefresh prop (new ref every render)In this example, typing in a search input re-renders the entire screen because the parent doesn't isolate state. DashboardStats and ActivityFeed are expensive and shouldn't be re-rendering at all. The fixes: extract SearchInput state into its own component, memoize filterTransactions, and wrap onRefresh in useCallback.
Fabric maintains a shadow tree — an off-screen representation of your UI that drives layout calculations via Yoga. The shadow tree and the actual native views should always agree. When they don't, you get layout desyncs: views jumping to incorrect heights on re-render, modals resizing unexpectedly, or touch targets not matching visual positions.
This is a known category of issues, particularly with navigation libraries. React-native-screens has documented cases where presenting a modal triggers rapid updateBounds calls that cause the shadow tree to go out of sync, resulting in incorrect heights on subsequent re-renders.
Isolate the trigger. Layout desyncs typically surface on re-render, not initial render. If the first render looks correct but subsequent renders break layout, you're likely hitting a shadow tree desync. Reduce your screen to the minimal reproduction — strip components until you find which one triggers the jump.
Check Animated components. Views using React Native's Animated components (Animated.View, TouchableOpacity) have been observed to cause double-rendering on load in the New Architecture, which can trigger layout recalculations. If removing an Animated wrapper fixes the layout, you've found the source.
Watch for native-side layout updates. Libraries that call updateState directly on shadow nodes from native code (outside a React render cycle) can desync the tree. This is a common pattern in navigation libraries that adjust view bounds when presenting or dismissing screens. Check GitHub issues for your specific library + "New Architecture."
collapsable={false} on views that need stable layout to prevent Fabric from optimizing them awayReanimated 4 shipped with CSS animations and transitions support, and Worklets are now a standalone package. But there are known performance regressions on Android when using Reanimated with the New Architecture enabled. Animations that ran smoothly on the old architecture now stutter and lag — in some cases severely enough to make the app feel unusable.
This affects Reanimated versions from 3.16 through 4.x. The issue is specific to Android; iOS performance is generally unaffected.
If you can't wait for a fix, consider whether the affected animations can use React Native's built-in Animated API or CSS-based animations (available in Reanimated 4) instead of worklet-driven animations. For screen transitions, check if your navigation library offers non-Reanimated animation options.
For critical paths, profile with the Performance panel to determine exactly how much time is being lost. Some teams have found that the stuttering is tolerable in non-critical animations but requires workarounds for hero transitions and gesture-driven interactions.
The old bridge and the New Architecture are fundamentally different in how they communicate between JavaScript and native code. Libraries built on the old bridge — using NativeModules and the legacy renderer — may crash, behave incorrectly, or silently fail on the New Architecture.
React Native Directory. The community maintains compatibility status for most popular libraries at reactnative.directory. Filter by "New Architecture" support to see which of your dependencies are verified, untested, or known incompatible.
expo-doctor. Run npx expo-doctor to automatically check your dependencies against the React Native Directory data. It flags unmaintained packages and those that are incompatible or untested with the New Architecture.
Runtime crash isolation. If your app crashes after migration and you're not sure which library is responsible, comment out providers and native modules one by one in your App component until the crash stops. This is Shopify's recommended approach — brute-force but reliable.
UIManager directly will break on Fabric. Look for "UIManager is not defined" or similar errorsIf you're in the middle of a migration (or just landed on the New Architecture via an SDK upgrade), here's a systematic approach:
Run npx expo-doctor and check React Native Directory before you start debugging runtime issues. There's no point profiling a performance regression if the root cause is an incompatible library.
If you haven't migrated yet, profile your critical screens on the old architecture first. Record render times, interaction latency, and crash-free session rates. Without a baseline, you won't know whether a post-migration issue is new or pre-existing.
Get the New Architecture running in your development environment first. Fix the obvious crashes and layout issues. Then use app store gradual rollout features (1% → 5% → 25% → 100%) to catch issues that only appear at scale or on specific devices.
Not all post-migration bugs have the same root cause. Before you start optimizing, determine which category you're in: performance regression (profile it), layout desync (isolate the trigger), animation stuttering (platform-check it), or library incompatibility (audit it). The fix depends entirely on the category.
The hardest post-migration bugs are the ones where something is "just slower" or "sometimes breaks" without an obvious cause. This is where runtime event correlation helps — linking a user interaction to the state update it triggered, to the components that re-rendered, to the time each render took. Tools like the Performance panel, React Profiler, or Limelight's correlation engine can surface these chains automatically.