Skip to main content

React Native engineer role-specific interview questions

30 questions on role-specific for react native engineer candidates. Expect prompts such as “How the old RN bridge works under the hood” and “FlatList internals and when FlashList wins”, each with a worked answer outline and the follow-ups interviewers push on.

Questions

Showing 1 to 25 of 30 role-specific questions (page 1 of 2).

As asked

Walk me through how the old React Native bridge works. What is serialized, what thread does each side run on, and what are the concrete performance consequences of crossing the bridge frequently?

Sample answer outline

A strong answer explains that the bridge is an asynchronous, batched, JSON-serialized channel between the JS thread and the native main thread. All messages are serialized to JSON, queued, and dispatched in batches, which means no synchronous calls are possible and large payloads or high-frequency calls create jank. The candidate should name the three threads (JS, native/main, shadow/layout) and explain that layout happens on the shadow thread via Yoga. They should mention that serialization cost is proportional to payload size, so passing large blobs or images over the bridge is expensive.

Expect these follow-ups

  • How does the new architecture eliminate the serialization bottleneck?
  • What debugging tool would you use to measure bridge traffic?
bridgearchitecturethreadingperformancejsthread

As asked

What windowing strategy does FlatList use, what causes blank cells during fast scroll, and when would you replace it with FlashList? What does FlashList do differently at the cell recycling level?

Sample answer outline

FlatList uses VirtualizedList under the hood and renders items into a scrollable container, unmounting items outside the window defined by windowSize and maxToRenderPerBatch. Blank cells during fast scroll happen when the JS thread cannot render new cells fast enough to fill the scroll velocity. FlashList (by Shopify) recycles cell components rather than unmounting/remounting them, similar to RecyclerView on Android, which avoids layout and reconciliation costs. A strong answer mentions that FlashList requires stable item types and heights for best performance and that it can render many more items per second than FlatList on mid-range devices.

Expect these follow-ups

  • How do you handle variable-height items in FlashList?
  • What is the recyclingKey prop and when do you need it?
flatlistflashlistvirtualizationperformancelists

As asked

Your team has an iOS-only feature (Live Activities) and an Android-only feature (predictive back gesture). What are the four ways React Native lets you write platform-specific code, and which would you use for each scenario?

Sample answer outline

The four strategies are: Platform.OS checks at runtime, Platform.select for conditional values, the .ios.tsx / .android.tsx file extension convention (Metro resolves the right file), and separate native modules per platform. For Live Activities you would write a dedicated iOS native module (Swift/ObjC) and expose it through a .ios.ts wrapper, with a no-op stub in .android.ts. For the predictive back gesture you would handle BackHandler on Android and likely write a native module or use a library that wraps BackEvent. A strong answer notes that file-extension splitting is the cleanest approach for large divergence, while Platform.select is fine for small style differences.

Expect these follow-ups

  • How do you test the iOS-only code path in a Jest unit test?
  • What happens in your CI pipeline when you run tests on a Linux machine and a component has an .ios.tsx file?
platform-specificiosandroidplatform.selectnative-modules

As asked

Walk me through the full path of a push notification from your backend server to the user's screen, naming every hop on iOS and Android separately. What can go wrong at each hop, and how do you debug a notification that silently never arrives?

Sample answer outline

On iOS: backend sends to APNS with the device token, APNS delivers to the device, iOS wakes the app or queues the notification. On Android: backend sends to FCM, FCM delivers via a persistent connection to the device, the app's FirebaseMessagingService receives it. Failure points: invalid or expired device tokens (must handle 410 responses from APNS), APNS certificate or p8 key misconfiguration, FCM project ID mismatch, doze mode or battery optimization on Android blocking FCM, notification permission not granted, and payload size limits (4 KB APNS, 4 KB FCM). Debugging involves checking APNS feedback, FCM delivery receipts, and @react-native-firebase/messaging event logs on device.

Expect these follow-ups

  • How do you handle background data messages vs notification messages on Android?
  • How do you test push notifications in a simulator where APNS is unavailable?
push-notificationsapnsfcmiosandroid

As asked

You are starting a new React Native app with 50 plus screens, a large authenticated user session, server state from a REST API, and local UI state. Walk me through how you would structure state management and which libraries you would and would not use.

Sample answer outline

A strong answer separates server state (React Query or SWR for caching, invalidation, and background refetching) from client/UI state (Zustand or Context for lightweight global state like theme, auth session, modals). Redux is often overkill for new projects but defensible for teams with Redux expertise or complex derived state needs. Jotai or Recoil suit fine-grained atom-level subscriptions. The candidate should note that mixing everything into Redux (including server cache) leads to cache invalidation complexity that React Query solves by design. They should also mention that React Native apps benefit from selecting minimal state slices to avoid unnecessary re-renders in lists.

Expect these follow-ups

  • How does React Query's staleTime interact with app backgrounding on mobile?
  • How do you persist Zustand state across app restarts?
state-managementreact-queryzustandreduxarchitecture

As asked

QA reports that on Android the keyboard covers the text input in a form, while on iOS it does not. You have KeyboardAvoidingView in place but it does not work consistently. How do you debug and fix this?

Sample answer outline

KeyboardAvoidingView behavior differs by platform: 'padding' works on iOS, 'height' or 'position' is sometimes needed on Android. The candidate should check whether the Android manifest has windowSoftInputMode set (adjustResize vs adjustPan) and whether that conflicts with KeyboardAvoidingView. They should know that with adjustResize, the OS resizes the window and KeyboardAvoidingView with padding is the right pair. With adjustPan, the OS scrolls the view and KeyboardAvoidingView can cause double offset. The recommended fix for modern apps is react-native-keyboard-controller, which handles this more reliably across both platforms. The answer should also mention testing on different Android API levels because soft input mode behavior changed in Android 14.

Expect these follow-ups

  • How does Android 15's predictive back gesture affect keyboard handling?
  • How do you handle a bottom sheet with a text input and keyboard avoidance?
situationalkeyboardandroidiosux

As asked

How do you hand off a motion-heavy interaction so engineers can implement it accurately across web and mobile?

Sample answer outline

The answer should include a prototype, but not rely on the prototype alone. Specify states, triggers, timing, easing curves, delays, interruption rules, token names, and fallback behaviour for low-power devices and reduced-motion users. Good candidates explain how they collaborate with engineers early to check platform constraints rather than throwing over an After Effects file. They should also mention acceptance criteria and a review loop using real builds, because motion often changes once it meets real data. The common trap is specifying pixels and frames without explaining the purpose of the movement.

Expect these follow-ups

  • What do you include in the spec for interrupted gestures?
  • How do you handle different animation APIs on iOS, Android, and web?
  • When would you accept an implementation that differs from your prototype?
handoffprototypingcollaboration

As asked

Explain what JSI is, how it differs from the old bridge, and what Fabric and TurboModules each solve. If you had a production app on the old architecture, what migration risks would you flag before enabling the new architecture?

Sample answer outline

JSI (JavaScript Interface) lets JS hold direct C++ references and call host objects synchronously without serialization. TurboModules use JSI to replace the module registry with lazy-loaded, type-safe native modules. Fabric replaces the shadow thread with a C++ renderer (Concurrent React aware) and enables synchronous layout reads. Migration risks include third-party libraries that still use the old bridge APIs, custom native modules that have not been converted to the Codegen spec, and subtle timing differences from synchronous calls that can expose race conditions that the async bridge was hiding.

Expect these follow-ups

  • How does Codegen fit into TurboModules?
  • What is the Interop Layer and when would you rely on it during migration?
new-architecturejsifabricturbomodulesmigration

As asked

I need to expose a Bluetooth scanning API to JavaScript on both iOS and Android. Walk me through building it as a TurboModule. What files do you create, what does the Codegen spec look like, and how does the JS side consume it?

Sample answer outline

The candidate should mention creating a Flow or TypeScript spec file (NativeBluetoothModule.ts) that extends TurboModuleRegistry, defining the exported function signatures so Codegen can produce C++ headers and Java/Kotlin stubs. On iOS they implement RCTTurboModule in Swift or ObjC; on Android they implement TurboModule in Kotlin. The JS side calls NativeModules.BluetoothModule or the typed NativeBluetoothModule directly. A strong answer notes that events from the native side (scan results) still go through the EventEmitter pattern, and callbacks or Promises should be used for async scanning results.

Expect these follow-ups

  • How do you handle memory leaks if the JS side is unmounted while a scan is in progress?
  • How do you test the native module in isolation without a device?
native-modulesturbomodulescodegeniosandroid

As asked

A screen in your app drops frames consistently during scroll but the native profiler shows the main thread is fine. How do you diagnose this, and what are the most common causes you would look for first?

Sample answer outline

The candidate should immediately identify that the JS thread is blocked, not the native thread, which shows up as dropped frames even though the main thread is idle. They should mention using the React Native Performance Monitor, Flipper, or Systrace to confirm JS thread frame times. Common causes are expensive render methods computing derived data on every frame, heavy JSON parsing during scroll, unthrottled scroll event handlers, or large list re-renders caused by reference-unstable props. The fix is usually memoization (useMemo, useCallback), moving computation off the render cycle, InteractionManager.runAfterInteractions for deferred work, or switching to a worklet via Reanimated to run animation logic on the UI thread.

Expect these follow-ups

  • How does useNativeDriver=true help, and what can it not animate?
  • When would you reach for Reanimated worklets instead of Animated API?
performancejs-threadprofilingreanimatedflatlist

As asked

Why does React Native ship with Hermes as the default JS engine instead of JavaScriptCore or V8? What specific optimizations does Hermes make for mobile, and what trade-offs does it introduce?

Sample answer outline

Hermes pre-compiles JS to bytecode at build time, which eliminates parse-and-compile delay at startup and reduces peak memory during launch. It is optimized for small binary footprint and fast TTI on constrained hardware. The trade-offs are that Hermes's JIT (added in RN 0.74) is less mature than V8 for sustained compute workloads, and some eval-based or dynamic code patterns are unsupported. A strong answer also notes that since RN 0.73 Flipper is no longer the default debugger; the Hermes debugger is now accessible through the built-in React Native DevTools (reachable via the Dev Menu or the new debugger frontend introduced in RN 0.73+), and source maps are used for symbolicated stack traces in Sentry and other crash reporters.

Expect these follow-ups

  • How do you profile Hermes heap usage to diagnose a memory leak?
  • Is there a case where you would disable Hermes and use JavaScriptCore instead?
hermesjavascript-enginestartupmemoryperformance

As asked

Walk me through what Metro does when you run npx react-native start and then request the bundle from a simulator. What is the dependency graph, how does HMR work, and what can you customize?

Sample answer outline

Metro resolves the entry point, builds a dependency graph by following require and import calls, transforms each module using Babel (or SWC with plugins), and serializes the graph into a single bundle or split bundles. The dev server serves the bundle over HTTP and uses websockets for HMR. Hot Module Replacement sends only changed module deltas. Candidates can customize Metro via metro.config.js: adding custom resolvers, transformers (for SVG, for example), blocklist patterns, and bundle splitting with RAM bundles or inline requires.

Expect these follow-ups

  • How do you set up a monorepo with Metro so it can resolve packages from a shared package?
  • What is an inline require and when does Metro insert them automatically?
metrobundlinghmrbuildmonorepo

As asked

What is a Reanimated worklet, what thread does it run on, and what restrictions apply inside a worklet function? Give a concrete example of a gesture-driven animation where worklets are necessary for a smooth 60fps experience.

Sample answer outline

A worklet is a function annotated with 'worklet' that Reanimated serializes and runs on the UI thread (or a dedicated Reanimated thread), completely bypassing the JS thread. Restrictions include no access to React state, no closure over non-shared values, and no async operations. A concrete example is a swipe-to-dismiss card: the gesture handler fires on the UI thread, the worklet computes the translated X position and opacity as shared values, and the animated style updates without any JS involvement, enabling 60fps even when the JS thread is busy. A strong answer distinguishes runOnJS (to call back into JS) from runOnUI.

Expect these follow-ups

  • How does useAnimatedScrollHandler differ from a plain onScroll prop?
  • What shared values can you safely pass from JS to a worklet?
reanimatedworkletsui-threadgesturesanimation

As asked

React Navigation's default JS stack and the native stack backed by react-native-screens have different performance characteristics. When do you choose each, what does react-native-screens actually do at the native layer, and what features does the native stack not support?

Sample answer outline

The JS stack renders screen components as views inside a single ViewController/Activity, animating them in JS. The native stack delegates to UINavigationController on iOS and FragmentTransaction on Android, giving you hardware-accelerated, platform-native transitions and proper gesture handling by the OS. The native stack does not support fully custom transition animations, arbitrary header components, or modal presentations that the JS stack can do with full React flexibility. A strong answer notes that react-native-screens also manages screen lifecycle (enableScreens()) to reduce memory by freezing offscreen screens, independent of which navigator you use.

Expect these follow-ups

  • How does enableFreeze differ from enableScreens in react-native-screens?
  • How do you handle deep links that need to navigate to a nested screen on cold start?
navigationreact-navigationnative-stackreact-native-screensperformance

As asked

You are building a field-service app where users must create and update job records while underground with no signal. Walk me through your offline-first data strategy: local storage choice, conflict resolution, sync queue design, and how you handle the reconnect event.

Sample answer outline

A strong answer picks a local DB (WatermelonDB for relational needs with lazy loading, or MMKV for key-value, or SQLite directly) and explains writing all mutations locally first with a pending sync queue. Each record gets a local UUID and a server ID (null until synced). On reconnect, a background sync process sends mutations in order, handles 409 conflicts with a strategy (last-write-wins, server-wins, or merge with user prompt), and marks records as synced. The candidate should mention NetInfo for connectivity detection, retry with exponential backoff, and idempotency keys on mutations so retransmitted requests do not duplicate records.

Expect these follow-ups

  • How do you handle a conflict where two users edited the same record offline at the same time?
  • How does WatermelonDB's sync protocol differ from building your own queue?
offlinesyncwatermelondbsqliteconflict-resolution

As asked

Your app stores user session tokens, large cached API responses, and a local analytics event queue. For each of those three use cases, which storage layer would you use and why? What are the concrete limits of AsyncStorage that MMKV avoids?

Sample answer outline

AsyncStorage is async-only, JS-thread bound, unencrypted, and has per-key and total size limits (6 MB default on Android). MMKV uses mmap and writes synchronously on a background thread, supports encryption with AES, and has no practical size ceiling for typical app data. For session tokens, MMKV with encryption is the right choice. For large cached API responses, MMKV or a SQLite-backed solution is better. For an event queue with ordering guarantees, SQLite or WatermelonDB is safer because MMKV does not natively support queuing semantics. A strong answer also mentions the Keychain (iOS) or Keystore (Android) for sensitive tokens.

Expect these follow-ups

  • How do you encrypt MMKV storage and rotate the encryption key?
  • What happens to AsyncStorage data when the app is uninstalled on Android vs iOS?
mmkvasyncstoragestorageencryptionperformance

As asked

Your Android APK is 80 MB and your iOS IPA is 70 MB. Walk me through the techniques you would apply on both platforms to get those numbers down, in rough order of impact.

Sample answer outline

On Android: publish an AAB instead of an APK so the Play Store splits by ABI and screen density, enable R8 minification and shrinking, strip unused native ABIs (arm64-v8a covers the vast majority of modern devices), and use split APKs by ABI in CI for non-Play-Store distribution. On iOS: strip unused architectures in the build settings, use asset catalog optimization, compress images at source, and note that bitcode was removed by Apple in Xcode 15 so it is no longer a lever. Across both platforms: Hermes produces pre-compiled bytecode which is smaller than shipping source and eliminates parse overhead, use Metro inline requires to defer loading of rarely-used modules, audit node_modules for large transitive dependencies using react-native-bundle-visualizer or source-map-explorer, tree-shake unused icon sets, and switch to WebP for image assets. A strong answer also mentions that AAB delivery via Play reduces the final installed size compared to a universal APK.

Expect these follow-ups

  • How do you measure what is contributing most to bundle size?
  • What is the impact of switching from PNG to WebP for images in the bundle?
app-sizeaabproguardhermesoptimization

As asked

After navigating between screens repeatedly, your app's memory footprint keeps growing and eventually crashes. Walk me through how you would diagnose and locate the leak using available tools.

Sample answer outline

Start with the React Native DevTools (accessible from the Dev Menu in RN 0.73+) and the Hermes debugger, which exposes heap snapshots you can take before and after navigation to diff retained objects. Chrome DevTools can also connect to the Hermes debugger for heap profiling. Common culprits are event listeners added in useEffect that are not cleaned up in the return function, NativeEventEmitter subscriptions that are never removed, native module callbacks that hold a reference to a JS component after it unmounts, large images kept in memory because require() loads them eagerly, and Redux or Zustand stores accumulating stale references. On Android, Android Studio's Memory Profiler can catch leaks at the native layer. A strong answer mentions wrapping NativeEventEmitter subscriptions in useEffect return functions, using WeakRef for non-critical references, and verifying that cleanup is called by writing a test that mounts and unmounts the component repeatedly while watching heap size.

Expect these follow-ups

  • How do you test for leaks in an automated CI pipeline?
  • What is the difference between a JS heap leak and a native memory leak in an RN app?
memory-leaksprofilingflipperhermesdebugging

As asked

You ship a hotfix via CodePush. Walk me through what happens at the device level: how is the new bundle downloaded and applied, what is the rollback mechanism, and what cannot be changed via CodePush?

Sample answer outline

Microsoft App Center (which hosted CodePush) was retired in March 2025, so teams must now use an alternative OTA update service. The two main options are EAS Update (from Expo, works with both bare and managed workflows) and a self-hosted solution such as the open-source hot-updater or a custom CDN-backed bundle server. The mechanics remain the same: a diff bundle (or full bundle) is downloaded to the app's writable directory. On next launch (or immediately with an IMMEDIATE install mode), the new bundle is loaded from that directory instead of the packaged asset. If the app crashes on startup after the update, the OTA client detects the failure and reverts to the previous bundle. Things that cannot be changed via OTA update: native code, pod or Gradle dependencies, permissions, assets compiled into the binary, and anything in the iOS or Android project layer. A strong answer mentions mandatory versus optional updates, release channels (staging versus production), version-locking an update to a specific binary version range, and the risk of a JS bundle that calls a native API not present in an older binary.

Expect these follow-ups

  • How do you version-lock a CodePush release to a specific binary version range?
  • What is your rollout strategy for a high-risk JS change: full rollout vs percentage?
codepushotaupdatesdeploymentrollback

As asked

You get a crash report from Sentry showing a native stack trace with memory addresses and a minified JS stack. Walk me through the full symbolication pipeline for both the native and JS sides.

Sample answer outline

For the JS side: the build uploads a source map to Sentry, and Sentry maps minified function names and line numbers back to original source. For iOS native crashes: dSYM files (generated at build time by Xcode) are uploaded to Sentry, which uses them to resolve binary addresses to function names and line numbers. For Android: the mapping.txt from ProGuard/R8 is uploaded for Java/Kotlin, and .so debug symbols are uploaded for native C++ crashes. A strong answer mentions that Hermes produces its own source maps and that you need to merge them with Metro source maps using hermes-parser for full fidelity.

Expect these follow-ups

  • How do you ensure dSYMs are uploaded automatically in your CI pipeline?
  • What would cause a JS crash to show as a native crash in the report?
crash-reportingsentrysymbolicationdsymsource-maps

As asked

You have a native Bluetooth module and a React component that calls it. How do you write unit tests for the component in Jest without a real device, and what does a meaningful integration test look like?

Sample answer outline

For Jest unit tests, you mock the native module using jest.mock or by adding a manual mock in __mocks__. The setup file provided by react-native sets up NativeModules stubs. You test the component's behavior (loading states, error handling, data rendering) against the mock. For integration tests, Detox runs on a real device or emulator and can drive the actual native module. A strong answer distinguishes what Jest can cover (JS logic, component rendering, error branches) from what Detox covers (actual native behavior, permissions flows, real Bluetooth scan). They might also mention using maestro for lighter-weight E2E tests.

Expect these follow-ups

  • How do you test the iOS and Android implementations of the native module independently?
  • What is the role of mock factories like jest.createMockFromModule for complex native modules?
testingjestdetoxnative-modulesmocking

As asked

Your app handles OAuth tokens. What are the attack surface differences between AsyncStorage, MMKV, and the platform Keychain/Keystore on iOS and Android? What is the minimum acceptable approach for storing a long-lived refresh token?

Sample answer outline

AsyncStorage stores data as plain text in an unencrypted SQLite file accessible to anyone with filesystem access on a jailbroken/rooted device. MMKV supports encryption but the key must be derived or stored somewhere accessible. The platform Keychain on iOS and Keystore on Android store secrets in hardware-backed secure enclaves where the key never leaves the hardware. For long-lived refresh tokens, the minimum acceptable approach is react-native-keychain backed by biometric authentication. A strong answer also mentions certificate pinning to prevent MITM, jailbreak/root detection as an extra layer, and that access tokens should be short-lived and stored in memory only.

Expect these follow-ups

  • How does biometric authentication work through react-native-keychain?
  • What is the risk of certificate pinning for app updates?
securitykeychaintokensoauthencryption

As asked

Your app loads hundreds of user avatars in a list. The Image component's built-in caching is unreliable. What strategies do you use for efficient image loading, caching, and memory management in a React Native list?

Sample answer outline

The default RN Image component caches to disk on iOS via NSCache and on Android similarly, but eviction is unpredictable. For production lists, the candidate should recommend fast-image (react-native-fast-image) which uses SDWebImage on iOS and Glide on Android, both with explicit memory and disk cache controls. They should also mention resizing images server-side or via CDN to match display size (never loading 2000x2000 avatars into a 40x40 cell), using blurhash or low-res placeholders, and clearing the cache on logout. A strong answer mentions that recycling cells in FlashList can cause image flickering if fast-image's memory cache is not large enough to hold visible cells.

Expect these follow-ups

  • How do you handle images behind an authenticated API (no public CDN URL)?
  • What is the difference between local and disk priority in fast-image cache settings?
imagescachingfast-imageglidesdwebimage

As asked

What problem does react-native-gesture-handler solve that the built-in TouchableOpacity does not, and why does running gesture recognition on the native thread matter for user experience? Give an example where the difference is visible to the user.

Sample answer outline

TouchableOpacity handles gestures on the JS thread, so if the JS thread is blocked by a heavy render or async operation, touch feedback is delayed. RNGH processes gestures on the native (UI) thread, meaning touch recognition and feedback (scale, opacity) happen even when JS is busy. A concrete example: in a camera preview screen, touching a shutter button implemented with RNGH fires instantly, while one built with TouchableOpacity would lag if the JS thread is processing a large photo. The candidate should also mention that RNGH integrates with Reanimated so gesture values can drive animations entirely on the UI thread.

Expect these follow-ups

  • What is the difference between a GestureDetector in RNGH v2 and the old wrapping approach?
  • How do you compose simultaneous gestures (pan + pinch) in RNGH v2?
gesture-handlernative-threadreanimatedperformancetouch

As asked

You need to sync data in the background every 15 minutes when the app is not in the foreground, on both iOS and Android. What APIs and libraries are available, and what are the platform-specific limitations you must design around?

Sample answer outline

On iOS, background execution is heavily restricted. You can use Background App Refresh (BGAppRefreshTask) for periodic work with a minimum interval that iOS decides, background fetch with short execution windows (~30 seconds), and silent push notifications to wake the app. On Android, WorkManager via @react-native-community/background-fetch or react-native-background-fetch handles periodic tasks with battery-aware scheduling. Exact 15-minute intervals are not guaranteed on either platform due to OS-level batching and Doze mode on Android. A strong answer flags that long-running JS in the background on iOS will be killed, so all sync work must be fast or delegated to a native extension (like a background URLSession on iOS).

Expect these follow-ups

  • How do you handle a background sync that takes longer than the allotted time on iOS?
  • What is WorkManager's role in Android's battery optimization and how does it differ from a foreground service?
background-tasksiosandroidworkmanagerapp-refresh