Interactive modules from setup and core UI through architecture, performance, testing, release, and interview depth. Trace the bridge, lists, sync, and ship with judgment.
Build an effective RN dev loop: Metro, Fast Refresh, device runtime, and debugging habits.
// learning path
Start with RN setup and core UI, then keyboard and async UI patterns, then compare old bridge vs New Architecture, and follow through render pipeline, hooks, lists, images, navigation, lifecycle, offline sync, push, and OTA. Each module builds interview vocabulary on top of real builder intuition.
Your RN dev loop is Metro bundling, Fast Refresh behavior, device runtime execution, and a disciplined debug process.
Metro serves the JavaScript bundle your app runs.
RN UI is a component tree that becomes native views. Layout is flexbox computed by Yoga, then committed by Fabric.
Yoga runs flexbox layout for RN views.
Forms feel broken on mobile when keyboard, focus, and layout resizing are handled incorrectly. Plan for those UX edges.
Keyboard handling is a layout responsibility, not just a component choice.
Accessible RN apps need labels, roles, logical focus, adequate touch targets, and real screen reader testing.
Icon buttons need accessibilityLabel.
The old React Native bridge serialized every JS→native call as async JSON. The New Architecture replaces it with JSI direct references, lazy TurboModules, and Fabric's C++ shadow tree, the three shifts every senior RN interview expects you to name.
Old bridge = async JSON queue; New Architecture = JSI direct refs.
Async data fetching is UI design: loading states, cancellation for stale responses, caching, and error recovery that does not race.
Render loading and error states as first-class UI.
Separate local UI state, global client state, and server cache. Use the smallest container that solves the sharing problem.
Local state by default; global only when multiple screens need it.
Local persistence is your offline foundation: hydrate fast, version your cache, write updates safely, and reconcile later with conflict handling.
Hydrate locally for a fast, offline-friendly first render.
Four pillars: JSI (how JS talks to C++), Fabric (how UI commits to native), TurboModules (how native APIs load), and Codegen (how bindings stay typed). A button tap crosses all four.
JSI = direct C++ refs; Fabric = shadow tree + commit; TurboModules = lazy native APIs.
setState on React Native is not 'update the DOM.' It's JS render → reconciler diff → Fabric shadow tree → Yoga layout → native commit. Jank is paid at every stage, not just in JavaScript.
RN output is shadow tree + native views, not DOM.
Debug with a process: reproduce reliably, inspect state, profile JS vs UI thread, isolate one variable, verify in release on device.
Write a minimal repro before refactoring.
Hermes ships precompiled bytecode for faster startup and lower memory. JavaScriptCore parsed source at runtime and was the legacy default before Hermes became standard in React Native.
Hermes optimizes startup and memory, not peak JIT throughput.
Hooks on RN follow the same rules as web, but the cost of mistakes is higher: unstable callbacks break FlatList row memoization, effects without cleanup cause fetch races, and fetching in render loops infinitely.
Hooks must run in consistent order every render.
FlatList virtualizes: only a window of rows mounts. getItemLayout, keyExtractor, memoized renderItem, and avoiding inline styles in rows are the interview checklist for 10,000-item feeds.
FlatList mounts a window, not the full dataset.
FlatList is the core virtualized list. FlashList adds stricter recycling and requires estimatedItemSize, trading setup discipline for fewer blank frames on huge feeds.
FlatList first; FlashList when profiling demands it.
Gesture Handler recognizes touches; Animated handles simple native-driven motion; Reanimated moves gesture math to the UI thread.
RNGH delivers reliable gesture recognition.
Images flow through memory → disk → network tiers. FastImage and SDWebImage/Glide wrap native caches. Skeleton placeholders and size hints prevent layout shift and wasted decode work.
Images cache in memory, then disk, then network.
Deep links parse URLs into route params, React Navigation resolves the screen stack, and cold start requires handling the initial URL separately from in-app Linking events.
Handle getInitialURL for cold start; addEventListener for warm.
Expo optimizes time-to-first-screen with managed workflow and EAS. Bare RN gives full native control from day one. Modern Expo is not a walled garden, prebuild generates ios/android when you need custom native code.
Expo managed = fast start; bare = full native freedom.
RN apps move through active, background, and inactive states. Pause sync on background, flush drafts, queue push handling, and resume carefully, OS may kill the process anytime in background.
Listen to AppState; pause work when backgrounded.
Push flow: app registers FCM/APNs token → backend stores it → event triggers send → OS delivers → foreground/background/killed each behave differently → tap navigates via deep link.
Register and refresh FCM/APNs tokens on the server.
Test in layers: fast Jest unit tests, RNTL component tests with mocks, and a thin E2E smoke suite for money paths.
Many unit tests, fewer component tests, thin E2E smoke.
Adding native capability in New Architecture: write a TurboModule spec in TypeScript, let Codegen emit bindings, implement shared logic in C++ and thin platform layers in Swift/Kotlin, register via JSI, call from JS with getEnforcing.
TurboModule spec in TypeScript drives Codegen output.
Reanimated runs animation math on the UI thread via worklets and shared values, avoiding per-frame React re-renders and JS bridge traffic during gestures.
Worklets run on the UI thread, not in React render.
Offline-first: optimistic UI, local outbox queue, sync on reconnect, idempotent server APIs, and explicit conflict resolution, not 'hope the network returns.'
Optimistic UI needs rollback or failed-state on error.
Layer transport pinning, secure token storage, device integrity checks, and short-lived credentials. AsyncStorage is not a secrets vault.
Refresh tokens belong in secure storage, not AsyncStorage.
Mobile release is PR gates, signed native builds, beta distribution, staged store rollout, and knowing when OTA is enough.
Block bad merges before expensive native builds.
OTA (EAS Update, CodePush) ships JavaScript bundle changes without store review. Native code, permissions, and SDK changes require a store build. Know what you can and cannot hotfix Friday at 6pm.
OTA updates JS/assets; native changes need store builds.
Filter by category or level, or browse the learning path above.
Your RN dev loop is Metro bundling, Fast Refresh behavior, device runtime execution, and a disciplined debug process.
Metro serves the JavaScript bundle your app runs.
RN UI is a component tree that becomes native views. Layout is flexbox computed by Yoga, then committed by Fabric.
Yoga runs flexbox layout for RN views.
Forms feel broken on mobile when keyboard, focus, and layout resizing are handled incorrectly. Plan for those UX edges.
Keyboard handling is a layout responsibility, not just a component choice.
Accessible RN apps need labels, roles, logical focus, adequate touch targets, and real screen reader testing.
Icon buttons need accessibilityLabel.
The old React Native bridge serialized every JS→native call as async JSON. The New Architecture replaces it with JSI direct references, lazy TurboModules, and Fabric's C++ shadow tree, the three shifts every senior RN interview expects you to name.
Old bridge = async JSON queue; New Architecture = JSI direct refs.
Async data fetching is UI design: loading states, cancellation for stale responses, caching, and error recovery that does not race.
Render loading and error states as first-class UI.
Separate local UI state, global client state, and server cache. Use the smallest container that solves the sharing problem.
Local state by default; global only when multiple screens need it.
Local persistence is your offline foundation: hydrate fast, version your cache, write updates safely, and reconcile later with conflict handling.
Hydrate locally for a fast, offline-friendly first render.
Four pillars: JSI (how JS talks to C++), Fabric (how UI commits to native), TurboModules (how native APIs load), and Codegen (how bindings stay typed). A button tap crosses all four.
JSI = direct C++ refs; Fabric = shadow tree + commit; TurboModules = lazy native APIs.
Adding native capability in New Architecture: write a TurboModule spec in TypeScript, let Codegen emit bindings, implement shared logic in C++ and thin platform layers in Swift/Kotlin, register via JSI, call from JS with getEnforcing.
TurboModule spec in TypeScript drives Codegen output.
setState on React Native is not 'update the DOM.' It's JS render → reconciler diff → Fabric shadow tree → Yoga layout → native commit. Jank is paid at every stage, not just in JavaScript.
RN output is shadow tree + native views, not DOM.
Debug with a process: reproduce reliably, inspect state, profile JS vs UI thread, isolate one variable, verify in release on device.
Write a minimal repro before refactoring.
Hermes ships precompiled bytecode for faster startup and lower memory. JavaScriptCore parsed source at runtime and was the legacy default before Hermes became standard in React Native.
Hermes optimizes startup and memory, not peak JIT throughput.
Hooks on RN follow the same rules as web, but the cost of mistakes is higher: unstable callbacks break FlatList row memoization, effects without cleanup cause fetch races, and fetching in render loops infinitely.
Hooks must run in consistent order every render.
FlatList virtualizes: only a window of rows mounts. getItemLayout, keyExtractor, memoized renderItem, and avoiding inline styles in rows are the interview checklist for 10,000-item feeds.
FlatList mounts a window, not the full dataset.
FlatList is the core virtualized list. FlashList adds stricter recycling and requires estimatedItemSize, trading setup discipline for fewer blank frames on huge feeds.
FlatList first; FlashList when profiling demands it.
Gesture Handler recognizes touches; Animated handles simple native-driven motion; Reanimated moves gesture math to the UI thread.
RNGH delivers reliable gesture recognition.
Reanimated runs animation math on the UI thread via worklets and shared values, avoiding per-frame React re-renders and JS bridge traffic during gestures.
Worklets run on the UI thread, not in React render.
Images flow through memory → disk → network tiers. FastImage and SDWebImage/Glide wrap native caches. Skeleton placeholders and size hints prevent layout shift and wasted decode work.
Images cache in memory, then disk, then network.
Deep links parse URLs into route params, React Navigation resolves the screen stack, and cold start requires handling the initial URL separately from in-app Linking events.
Handle getInitialURL for cold start; addEventListener for warm.
Expo optimizes time-to-first-screen with managed workflow and EAS. Bare RN gives full native control from day one. Modern Expo is not a walled garden, prebuild generates ios/android when you need custom native code.
Expo managed = fast start; bare = full native freedom.
RN apps move through active, background, and inactive states. Pause sync on background, flush drafts, queue push handling, and resume carefully, OS may kill the process anytime in background.
Listen to AppState; pause work when backgrounded.
Offline-first: optimistic UI, local outbox queue, sync on reconnect, idempotent server APIs, and explicit conflict resolution, not 'hope the network returns.'
Optimistic UI needs rollback or failed-state on error.
Push flow: app registers FCM/APNs token → backend stores it → event triggers send → OS delivers → foreground/background/killed each behave differently → tap navigates via deep link.
Register and refresh FCM/APNs tokens on the server.
Layer transport pinning, secure token storage, device integrity checks, and short-lived credentials. AsyncStorage is not a secrets vault.
Refresh tokens belong in secure storage, not AsyncStorage.
Test in layers: fast Jest unit tests, RNTL component tests with mocks, and a thin E2E smoke suite for money paths.
Many unit tests, fewer component tests, thin E2E smoke.
Mobile release is PR gates, signed native builds, beta distribution, staged store rollout, and knowing when OTA is enough.
Block bad merges before expensive native builds.
OTA (EAS Update, CodePush) ships JavaScript bundle changes without store review. Native code, permissions, and SDK changes require a store build. Know what you can and cannot hotfix Friday at 6pm.
OTA updates JS/assets; native changes need store builds.