# React Native App Development in 2026: Complete Guide and Interview Questions > Master React Native app development in 2026 with the New Architecture, Hermes V1, and Fabric. Includes practical code examples and common interview questions. - Published: 2026-09-06 - Updated: 2026-09-06 - Author: Anthony Fillion-Maillet - Tags: react-native, mobile-development, javascript, typescript, new-architecture - Reading time: 12 min --- React Native app development in 2026 has fundamentally changed with version 0.87 now shipping the New Architecture as the only supported option. The legacy bridge that once serialized every JavaScript-to-native call as JSON is gone, replaced by JSI (JavaScript Interface) for direct synchronous communication. > **What Changed in React Native 0.87** > > React Native 0.87 makes the New Architecture mandatory. The old bridge is completely removed. Complex list rendering runs 43% faster, scroll frame drops decreased by 95%, and memory usage dropped 33% in benchmark tests. ## Understanding JSI and the New Architecture JSI (JavaScript Interface) replaces the asynchronous bridge with direct C++ bindings between JavaScript and native code. Instead of serializing data to JSON, passing it through a message queue, and deserializing on the other side, JSI allows JavaScript to hold references to native objects and call their methods directly. This architectural shift enables three core capabilities: synchronous native calls, shared ownership of objects between JavaScript and native layers, and lazy loading of native modules. ```typescript // NativeUserModule.ts import { TurboModuleRegistry, TurboModule } from 'react-native'; export interface Spec extends TurboModule { getUserProfile(userId: string): Promise<{ id: string; name: string; email: string; }>; // Synchronous call - only possible with JSI getDeviceLocale(): string; } export default TurboModuleRegistry.getEnforcing('UserModule'); ``` The `getDeviceLocale()` method returns immediately without awaiting a Promise. This was impossible with the old bridge where every native call required async serialization. ## Building a Production-Ready Component with Fabric Fabric is the new rendering system that works alongside JSI. It manages the UI tree in C++ and synchronizes directly with the JavaScript thread, eliminating the "UI jank" that occurred when the old bridge fell behind during rapid updates. ```typescript // ProductCard.tsx import React, { memo, useCallback } from 'react'; import { View, Text, Image, Pressable, StyleSheet, } from 'react-native'; import Animated, { useSharedValue, useAnimatedStyle, withSpring, } from 'react-native-reanimated'; interface Product { id: string; name: string; price: number; imageUrl: string; inStock: boolean; } interface ProductCardProps { product: Product; onPress: (id: string) => void; } const AnimatedPressable = Animated.createAnimatedComponent(Pressable); export const ProductCard = memo(function ProductCard({ product, onPress, }: ProductCardProps) { const scale = useSharedValue(1); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], })); const handlePressIn = useCallback(() => { scale.value = withSpring(0.95); }, [scale]); const handlePressOut = useCallback(() => { scale.value = withSpring(1); }, [scale]); const handlePress = useCallback(() => { onPress(product.id); }, [onPress, product.id]); return ( {product.name} ${product.price.toFixed(2)} {!product.inStock && ( Out of Stock )} ); }); const styles = StyleSheet.create({ card: { backgroundColor: '#ffffff', borderRadius: 12, overflow: 'hidden', marginHorizontal: 8, marginVertical: 4, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 3, }, image: { width: '100%', height: 160, }, content: { padding: 12, }, name: { fontSize: 16, fontWeight: '600', color: '#1a1a1a', marginBottom: 4, }, price: { fontSize: 18, fontWeight: '700', color: '#2563eb', }, outOfStock: { fontSize: 12, color: '#dc2626', marginTop: 4, }, }); ``` This component uses `react-native-reanimated` which runs animations on the UI thread via JSI. The `useSharedValue` and `useAnimatedStyle` hooks communicate directly with native code without crossing the bridge. ## State Management with TanStack Query Server state in React Native applications benefits from dedicated tooling. [TanStack Query](https://tanstack.com/query/latest) handles caching, background refetching, and optimistic updates without requiring a global state container. ```typescript // hooks/useProducts.ts import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; interface Product { id: string; name: string; price: number; imageUrl: string; inStock: boolean; } const API_BASE = 'https://api.example.com'; async function fetchProducts(category?: string): Promise { const url = category ? `${API_BASE}/products?category=${category}` : `${API_BASE}/products`; const response = await fetch(url); if (!response.ok) { throw new Error('Failed to fetch products'); } return response.json(); } export function useProducts(category?: string) { return useQuery({ queryKey: ['products', category], queryFn: () => fetchProducts(category), staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 30 * 60 * 1000, // 30 minutes }); } export function useToggleStock() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (productId: string) => { const response = await fetch(`${API_BASE}/products/${productId}/toggle-stock`, { method: 'POST', }); if (!response.ok) { throw new Error('Failed to update stock'); } return response.json(); }, // Optimistic update onMutate: async (productId) => { await queryClient.cancelQueries({ queryKey: ['products'] }); const previousProducts = queryClient.getQueryData(['products']); queryClient.setQueryData(['products'], (old) => old?.map((p) => p.id === productId ? { ...p, inStock: !p.inStock } : p ) ); return { previousProducts }; }, onError: (_err, _productId, context) => { if (context?.previousProducts) { queryClient.setQueryData(['products'], context.previousProducts); } }, onSettled: () => { queryClient.invalidateQueries({ queryKey: ['products'] }); }, }); } ``` The `onMutate` callback performs an optimistic update before the server responds. If the mutation fails, `onError` restores the previous state. This pattern delivers instant UI feedback while maintaining data consistency. ## Navigation with Expo Router Expo Router provides file-based routing for React Native, similar to Next.js App Router. The file structure defines routes automatically. ```typescript // app/(tabs)/_layout.tsx import { Tabs } from 'expo-router'; import { Home, Search, ShoppingCart, User } from 'lucide-react-native'; export default function TabsLayout() { return ( ( ), }} /> ( ), }} /> ( ), }} /> ( ), }} /> ); } ``` Dynamic routes use bracket notation. A file at `app/product/[id].tsx` handles any `/product/123` path: ```typescript // app/product/[id].tsx import { useLocalSearchParams } from 'expo-router'; import { View, Text, ActivityIndicator } from 'react-native'; import { useQuery } from '@tanstack/react-query'; export default function ProductDetailScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const { data: product, isLoading, error } = useQuery({ queryKey: ['product', id], queryFn: () => fetchProduct(id), enabled: !!id, }); if (isLoading) { return ( ); } if (error || !product) { return ( Product not found ); } return ( {product.name} ${product.price.toFixed(2)} ); } ``` ## Common Interview Questions on React Native Architecture Technical interviews for React Native positions in 2026 focus heavily on the New Architecture. The following questions appear frequently. > **Interview Focus Areas** > > Interviewers expect candidates to explain JSI, Fabric, and TurboModules at a conceptual level. Memorizing syntax matters less than understanding why the architecture changed and what problems it solves. **Question: What is JSI and why did React Native adopt it?** JSI (JavaScript Interface) is a C++ layer that allows JavaScript to hold references to native objects and call their methods directly. The old bridge serialized every call to JSON, queued it asynchronously, and deserialized on the native side. This added latency and prevented synchronous operations. With JSI, a JavaScript function can call a native method and receive the result in the same frame. This enables features like synchronous layout measurements and direct manipulation of native views. **Question: How does Fabric differ from the old renderer?** Fabric is a C++ rendering system that maintains the UI tree in native code rather than JavaScript. The old renderer kept the shadow tree in JavaScript and sent update batches across the bridge. Fabric benefits: - Synchronous layout calculations via JSI - Concurrent rendering support (React 18 features work properly) - Better memory management with shared ownership between JS and native - Reduced serialization overhead **Question: What are TurboModules?** TurboModules are the replacement for Native Modules. The key differences: 1. **Lazy loading**: TurboModules load only when first accessed, reducing startup time 2. **Type safety**: CodeGen generates type-safe bindings from TypeScript specs 3. **Synchronous methods**: TurboModules can expose synchronous functions when needed 4. **Direct JSI access**: No bridge serialization **Question: When would you use a synchronous native method?** Synchronous native methods suit operations that: - Complete in microseconds (reading device locale, checking permissions status) - Block UI intentionally (modal confirmation before navigation) - Return values needed for immediate rendering Avoid synchronous methods for: - Network requests - File I/O beyond small reads - Database queries - Any operation exceeding 16ms (blocks the frame) For more React Native interview questions, see the [networking and API module](/technologies/react-native/interview-questions/rn-networking-api) and [native modules section](/technologies/react-native/interview-questions/rn-native-modules). ## Testing React Native Components React Native Testing Library provides utilities for testing components as users interact with them, focusing on accessibility and behavior rather than implementation details. ```typescript // __tests__/ProductCard.test.tsx import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react-native'; import { ProductCard } from '../components/ProductCard'; const mockProduct = { id: 'prod-1', name: 'Wireless Headphones', price: 149.99, imageUrl: 'https://example.com/headphones.jpg', inStock: true, }; describe('ProductCard', () => { it('displays product information', () => { const onPress = jest.fn(); render(); expect(screen.getByText('Wireless Headphones')).toBeOnTheScreen(); expect(screen.getByText('$149.99')).toBeOnTheScreen(); }); it('calls onPress with product id when pressed', () => { const onPress = jest.fn(); render(); fireEvent.press( screen.getByRole('button', { name: /wireless headphones/i }) ); expect(onPress).toHaveBeenCalledWith('prod-1'); expect(onPress).toHaveBeenCalledTimes(1); }); it('shows out of stock message when product unavailable', () => { const outOfStockProduct = { ...mockProduct, inStock: false }; render(); expect(screen.getByText('Out of Stock')).toBeOnTheScreen(); }); }); ``` The tests query by text content and accessibility roles rather than test IDs or component internals. This approach catches regressions that affect users while remaining resilient to refactoring. ## Performance Monitoring in Production React Native 0.87 integrates with [Sentry](https://docs.sentry.io/platforms/react-native/) and similar tools for production performance monitoring. Key metrics to track: - **Time to Interactive (TTI)**: How long until the app responds to user input - **Frame drops**: Frames that take longer than 16.67ms to render - **JavaScript thread usage**: High usage indicates expensive computations blocking the UI - **Memory growth**: Steady increases suggest leaks ```typescript // app/_layout.tsx import * as Sentry from '@sentry/react-native'; import { useEffect } from 'react'; import { AppState, AppStateStatus } from 'react-native'; Sentry.init({ dsn: process.env.EXPO_PUBLIC_SENTRY_DSN, tracesSampleRate: 0.2, profilesSampleRate: 0.1, enableAutoPerformanceTracing: true, }); export default function RootLayout() { useEffect(() => { const subscription = AppState.addEventListener( 'change', (state: AppStateStatus) => { if (state === 'active') { Sentry.addBreadcrumb({ category: 'app.lifecycle', message: 'App became active', level: 'info', }); } } ); return () => subscription.remove(); }, []); // ... rest of layout } ``` ## Key Takeaways for React Native Development in 2026 - The New Architecture (JSI, Fabric, TurboModules) is mandatory in React Native 0.87. The legacy bridge no longer exists. - JSI enables synchronous native calls. Use them sparingly for operations under 16ms. - Fabric renders UI in C++ and synchronizes with JavaScript via JSI. This eliminates the async rendering bottleneck. - TurboModules load lazily and provide type-safe bindings generated from TypeScript specs. - [Expo Router](https://docs.expo.dev/router/introduction/) offers file-based navigation similar to Next.js App Router. - React Native Testing Library tests components through their public API and accessibility tree. - Production apps should track TTI, frame drops, and memory usage from day one. For a deeper look at building complete applications, see the [React Native complete app tutorial](/blog/react-native/react-native-building-complete-mobile-app). The [New Architecture guide](/blog/react-native/react-native-new-architecture-hermes-v1-bridgeless) covers migration from older versions. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/en/blog/react-native/react-native-app-development-2026-complete-guide