React Native Interview Questions

Reviewed by Mark Dickie · Last updated

React Native is an open-source framework from Meta that lets you build mobile apps for iOS and Android using JavaScript and React, rendering to native UI elements rather than a web view. For interviews, you need a firm grasp of the component lifecycle, the Bridge and the new Fabric architecture, navigation patterns, performance tuning, and platform-specific code. Expect questions that test whether you understand what happens under the hood when you write a <View> — not just the JSX syntax.

What does a React Native interview typically test?

AreaWhat gets asked
Component lifecycle & hooksuseEffect cleanup, dependency arrays, useCallback to prevent re-renders
Bridge vs. FabricHow JS and native threads communicate; what the Fabric renderer changes
NavigationStack, tab, and drawer patterns with React Navigation; deep linking
PerformanceFlatList vs. ScrollView, React.memo, eliminating re-renders, Hermes engine
Platform-specific codePlatform.OS checks, native modules, linking native dependencies
Debugging & toolingReact Native DevTools, Metro bundler, handling red-screen errors

How should you structure your preparation?

  1. Solidify the React fundamentals first — hooks, context, and reconciliation. If your React knowledge has gaps, they will show up under React Native interview pressure.
  2. Study the threading model: the JS thread, the native UI thread, and how the Bridge (or JSI in the New Architecture) moves data between them.
  3. Build a small app with navigation, a networked list, and at least one native module. Being able to talk through a real project beats memorising API names.
  4. Review the New Architecture (Fabric, TurboModules, JSI) at a conceptual level. Interviewers increasingly ask about it even if their own apps have not migrated.
  5. Practise live, timed questions so you can talk through your reasoning out loud — the format below lets you do exactly that.

At a glance

Questions18
Difficulty1–5 of 5
FormatsTrue / false, Multiple choice, Fill in the blank, Multiple answer, Find the bug

What you'll review

  1. core components
  2. styling layout
  3. navigation
  4. lists performance
  5. native modules
  6. platform apis

Practice questions

React Native/core-components

True or False: In React Native, a ScrollView renders all of its child components at mount time, while a FlatList uses virtualization to render only the items currently visible (or near the viewport), which generally makes FlatList more memory-efficient for long lists.

Show answer

True. A ScrollView mounts and renders all children up front, which is acceptable for short, bounded content but costly for large lists. A FlatList virtualizes its rows, rendering only items near the visible viewport and recycling off-screen components, so its memory footprint stays roughly constant as data grows.

Why:

ScrollView lays out and renders every child immediately — fine for small, fixed collections but memory-heavy for hundreds of rows. FlatList is built on VirtualizedList under the hood: it mounts only the window of items near the viewport and recycles off-screen rows, so memory usage stays roughly constant as the data set grows.

React Native/styling-layout

In React Native, what is the primary mechanism for applying styles to a core component such as View or Text?

Options

  • Using the CSS class name in a separate stylesheet file
  • Using Tailwind utility classes compiled at build time
  • Using the style prop, which accepts a plain JavaScript object or an array of objects
  • Using the className prop with a CSS-in-JS library only
Show answer

React Native components are styled primarily through the style prop, which accepts a plain JavaScript object of style key-value pairs or an array of such objects. This differs from web development, where CSS class names or utility frameworks are used. The styles are typically defined using StyleSheet.create for performance and validation.

Why:

React Native components are styled via the style prop, which takes a plain JavaScript object of style key-value pairs (commonly created with StyleSheet.create) or an array of such objects. Unlike web React, there are no CSS class names or Tailwind classes built in, and className alone does not apply styles in core React Native.

React Native/navigation

In a React Navigation Stack navigator (React Navigation v6/v7), what is the key behavioral difference between calling navigation.navigate('Details') and navigation.push('Details') when a Details screen is already present in the stack?

Options

  • navigate pushes a new screen every time it is called; push only navigates to an existing instance if one is already on the stack.
  • navigate jumps to an existing instance of the screen if one is already on the stack, adding a new one only if none exists; push always adds a new instance on top of the stack.
  • Both behave identically; push is just an alias for navigate provided for web-developer familiarity.
  • navigate can only move backward in the stack, while push can only move forward.
Show answer

Use navigation.navigate('Details') when you want to reuse an existing instance of that screen on the stack — it jumps to it if found and only pushes a new one if none exists. Use navigation.push('Details') when you always want a fresh instance stacked on top, even if the screen is already present. This distinction is specific to the Stack navigator in React Navigation v6/v7.

Why:

In React Navigation's Stack navigator, navigate('Details') first searches the existing stack for a screen named Details; if found, it pops or jumps to that instance instead of creating a duplicate. If no such screen exists, it pushes one. push('Details') always pushes a brand-new instance onto the stack regardless of whether a Details screen is already there. This matters for scenarios like viewing the same detail page repeatedly without deduplication.

React Native/lists-performance

A React Native FlatList renders 10,000 items, each with a fixed row height of 80 pixels. You call flatListRef.current.scrollToIndex({ index: 5000 }), but React Native throws the error: "scrollToIndex should be used in conjunction with getItemLayout." To fix the error and gain a performance boost (because the list can skip measuring every row), add the _____ prop to the FlatList, implementing it as: (_, index) => ({ length: 80, offset: 80 * index, index })

Show answer

A React Native FlatList renders 10,000 items, each with a fixed row height of 80 pixels. You call flatListRef.current.scrollToIndex({ index: 5000 }), but React Native throws the error: "scrollToIndex should be used in conjunction with getItemLayout."

To fix the error and gain a performance boost (because the list can skip measuring every row), add the getItemLayout prop to the FlatList, implementing it as:

(_, index) => ({ length: 80, offset: 80 * index, index })
Why:

getItemLayout provides the exact pixel offset and length of every item without the FlatList needing to render and measure each one. This is mandatory for scrollToIndex because the list must compute the scroll position mathematically; without it, the runtime cannot know where item 5000 lives and throws the documented error. As a bonus, the list skips on-mount layout measurement of off-screen cells, improving scroll performance for large fixed-height lists.

React Native/native-modules

In a React Native bare project (RN 0.71+, legacy bridge architecture), you want to expose a native module on Android using Kotlin so that a method is callable from JavaScript and runs on the native modules background thread by default. Which combination is correct? class CounterModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { override fun getName() = "Counter" @ReactMethod fun increment(amount: Int, promise: Promise) { promise.resolve(amount + 1) } } Given the module class above, what else is required for the method increment to be callable from JS as NativeModules.Counter.increment(...) and to execute on the native modules thread (not the UI thread, not the JS thread)?

Options

  • Register the module via a class implementing ReactPackage (added to getPackages() in MainApplication); no thread annotation is needed because @ReactMethod methods run on a background thread by default.
  • Register via ReactPackage AND annotate increment with @ReactMethod(isBlockingSynchronousMethod = true) to ensure it runs off the UI thread.
  • Register via ReactPackage AND annotate the method with @UiThread so the native modules thread is used instead of the JS thread.
  • No ReactPackage is needed; @ReactMethod alone auto-registers the module. Annotate with @ReactMethod(isBlockingSynchronousMethod = true) for background execution.
Show answer

Register the module through a ReactPackage (returned from getPackages() in MainApplication) and rely on the default: @ReactMethod methods run on the native modules background thread automatically. No @UiThread or isBlockingSynchronousMethod annotation is needed. The synchronous flag would force execution on the JS thread, and @UiThread would force it onto the UI thread — both are the opposite of what you want.

Why:

On Android, a native module must extend ReactContextBaseJavaModule, annotate callable methods with @ReactMethod, and be registered through a ReactPackage whose createNativeModules() returns an instance of the module. By default, @ReactMethod (with isBlockingSynchronousMethod = false, the default) dispatches the method on the native modules background thread — not the UI thread and not the JS thread. Option (b) is wrong because isBlockingSynchronousMethod = true forces execution on the JS thread synchronously, which is the opposite of background. Option (c) is wrong because @UiThread forces the method onto the UI thread, not a background thread. Option (d) is wrong because @ReactMethod alone does not auto-register the module — a ReactPackage is required — and isBlockingSynchronousMethod = true forces synchronous JS-thread execution.

React Native/platform-apis

In a React Native project running on react-native-web, what value does the following expression evaluate to? Platform.select({ ios: 'A', android: 'B', default: 'C', })

Options

  • 'A'
  • 'B'
  • 'C'
  • undefined
Show answer

The expression evaluates to 'C'. In react-native-web, Platform.OS is 'web', and Platform.select returns the value for the key matching the current platform, falling back to default when that key is absent. Since there is no 'web' key in the object, the default entry ('C') is returned.

Why:

react-native-web sets Platform.OS to 'web'. Platform.select looks up the key matching the current platform; when no 'web' key exists it falls back to the special default key, which here maps to 'C'. It does not return undefined because default is always consulted before returning nothing.

React Native/core-components

The fundamental building block for UI layout in React Native is the _____ component. It maps to the native platform equivalent — UIView on iOS and android.view.View on Android — and supports Flexbox-based styling via the style prop.

Show answer

The fundamental building block for UI layout in React Native is the View component. It maps to the native platform equivalent — UIView on iOS and android.view.View on Android — and supports Flexbox-based styling via the style prop.

Why:

View is the core container primitive in React Native. Every layout — whether wrapping text, images, or other components — is composed of View nodes that map directly to native views, and React Native applies Flexbox rules (via Yoga) to position them.

React Native/styling-layout

Consider this React Native layout: <View style={{ flex: 1, flexDirection: 'row' }}> <View style={{ flex: 1, backgroundColor: 'red' }} /> <View style={{ flex: 2, backgroundColor: 'blue' }} /> </View> The parent has flex: 1 (filling the screen) and flexDirection: 'row'. How is the horizontal space divided between the red and blue children?

Options

  • Red gets 1/3 and blue gets 2/3 of the container's available horizontal space.
  • Each child gets 50% because flex values are interpreted as equal shares by default.
  • Red gets 1px, blue gets 2px — flex values are absolute pixel sizes in React Native.
  • Both children collapse to 0 width because no explicit width is set.
Show answer

The red child gets one-third and the blue child gets two-thirds of the container's available horizontal space. In React Native's Flexbox engine (Yoga), a numeric flex value is a grow factor: the total grow is 1 + 2 = 3, so each child's share is its own flex value divided by that total. Because flexDirection: 'row' sets the main axis to horizontal, the proportions apply along the row's width.

Why:

In React Native (which uses Yoga / Flexbox), the flex property sets a flex grow factor proportional to the sibling flex values. The total flex is 1 + 2 = 3, so the red child receives 1/3 of the available main-axis (horizontal) space and the blue child receives 2/3. flexDirection: 'row' makes the main axis horizontal; without explicit width the children still fill the row because their grow factors are non-zero. justifyContent would only matter if there were leftover free space, but here the flex factors consume all of it.

React Native/navigation

You are using React Navigation v7 with a native Stack Navigator. The current navigation stack (from bottom to top) is: [Home, Profile, Settings] — meaning Settings is the active screen. From the Settings screen you call navigation.navigate('Profile'). No getId prop is customised on the Profile route. What is the resulting navigation stack after this call?

Options

  • [Home, Profile, Settings, Profile] — a new Profile instance is pushed on top of Settings.
  • [Home, Profile] — React Navigation navigates back to the existing Profile instance, popping Settings off the stack.
  • [Home, Profile, Settings] — the stack is unchanged because Profile is already present and not the active screen.
  • [Profile] — the stack is reset so that Profile becomes the sole screen.
Show answer

The resulting stack is [Home, Profile]. In React Navigation's Stack Navigator, navigate('Profile') finds the existing Profile route already in the stack and pops back to it, removing Settings from the top. push('Profile'), by contrast, would always add a new Profile instance on top, producing [Home, Profile, Settings, Profile].

Why:

In React Navigation's Stack Navigator, navigate('ScreenName') searches the existing stack for a route with that name. If found, it pops back to that existing route rather than creating a new instance. Here Profile already sits in the stack below Settings, so calling navigate('Profile') pops Settings and focuses the existing Profile screen, yielding the stack [Home, Profile]. By contrast, push('Profile') would always create a new Profile instance on top, producing [Home, Profile, Settings, Profile]. Option (a) describes push behavior, (c) is wrong because navigate does change the active screen, and (d) is wrong because navigate never resets the entire stack (that would require navigation.reset).

React Native/lists-performance

A FlatList in a React Native 0.74 app renders 5,000 rows, each backed by a complex custom component. Scrolling is janky and the app's memory footprint is high. Which of the following changes will measurably help address these problems?

Options

  • Wrap the row component in React.memo so rows whose props are unchanged skip re-rendering
  • Reduce windowSize from its default (21) to a smaller value such as 5
  • Provide a getItemLayout function so item positions are computed in O(1) instead of measured at runtime
  • Set initialNumToRender to the full data length so every row mounts immediately on first render
  • Supply a stable keyExtractor that returns a unique, persistent id per row
Show answer

The helpful changes are: wrapping the row in React.memo (skips re-renders of unchanged rows), reducing windowSize (fewer offscreen items mounted, less memory and render work), providing getItemLayout (O(1) position calculation, no runtime layout measurement), and supplying a stable unique keyExtractor (prevents unnecessary unmount/remount on data changes). Setting initialNumToRender to the full data length is harmful — it defeats virtualization, rendering every row synchronously on first paint.

Why:

Each correct option targets a distinct layer of the virtualization pipeline. (a) React.memo on the row prevents React from re-rendering every visible row when the parent re-renders — without it, a parent state change re-renders all 5,000 row instances that are currently mounted. (b) Lowering windowSize reduces the number of offscreen items FlatList keeps mounted on either side of the viewport, directly cutting memory usage and the render work that happens during scroll. (c) getItemLayout lets VirtualizedList compute an item's pixel offset and length arithmetically, eliminating the native layout-measurement pass for every item and enabling O(1) scrollToIndex. (e) A stable, unique keyExtractor lets React's reconciler match existing row instances across data changes; without it, items may unmount and remount unnecessarily, thrashing memory and causing jank. (d) is the opposite of an optimization: setting initialNumToRender to the full data length defeats virtualization by rendering all 5,000 rows synchronously on first paint, spiking memory and blocking the main thread.

React Native/platform-apis

A React Native app running on Android 11 (API level 30) and above calls Linking.canOpenURL('myapp://deeplink') to check whether a third-party app can handle a custom URI scheme. The target app is installed and its intent-filter matches the scheme, yet canOpenURL consistently resolves to false. The same code works on Android 10 and below. What is the cause?

Options

  • Linking.canOpenURL only supports http and https schemes on Android; custom deep-link schemes always resolve to false regardless of manifest configuration.
  • Android 11 introduced package-visibility restrictions; the calling app must declare a <queries> element in AndroidManifest.xml with the target's <intent> filter or <package> name for canOpenURL to see it.
  • The calling app must register its own intent-filter for the target scheme in its own AndroidManifest.xml before Linking.canOpenURL can detect a third-party handler.
  • canOpenURL requires the target app to export a ContentProvider with the matching authority; without it Android silently drops the query.
Show answer

On Android 11 (API 30+) the call returns false because of package-visibility restrictions introduced in Android 11. The OS hides other installed apps from resolveActivity/queryIntentActivities unless the calling app declares a <queries> element in AndroidManifest.xml — via <intent> (matching the scheme) or <package> (naming the target). Add that declaration and Linking.canOpenURL resolves correctly. This is not a scheme-type limitation or a ContentProvider requirement.

Why:

Starting with Android 11 (API 30), the OS enforces package-visibility filtering: an app can no longer see, query, or resolve intents targeting other installed apps by default. Linking.canOpenURL ultimately calls resolveActivity / queryIntentActivities on the package manager, which returns an empty result under these restrictions unless the calling app explicitly declares visibility via a <queries> element in AndroidManifest.xml — using <intent> to match the scheme, <package> to name the target, or <provider> for authority-based queries. Once the <queries> declaration is added, canOpenURL resolves correctly. Option A is wrong because custom schemes are supported; the issue is visibility, not scheme type. Option C is wrong because registering your own intent-filter for the scheme would make your app a handler, not enable detection of another app. Option D is wrong because ContentProviders are unrelated to custom-scheme intent resolution.

React Native/native-modules

In React Native's New Architecture (TurboModules + JSI), a TurboModule exposes a native method that is declared synchronous in its Codegen spec. When JavaScript calls this method, the native implementation needs to create and return a jsi::Object by calling jsi::Object::setProperty on the jsi::Runtime& it received as its first argument. Which statement correctly describes the threading context and its safety implications?

Options

  • It executes on the JS thread (the thread that owns the Runtime), so accessing the Runtime inside the method body is safe without additional synchronization.
  • It executes on whichever native thread the TurboModule implementation chooses; accessing the Runtime always requires a mutex because the call may land on a non-JS thread.
  • The JSI Runtime is internally thread-safe, so any native thread can create jsi::Object or jsi::Function instances on it without synchronization.
  • TurboModule methods are always dispatched to the native-modules thread pool (as in the legacy bridge), so Runtime access is never safe and must be deferred to the JS thread.
Show answer

Synchronous JSI calls from JavaScript execute on the JS thread—the same thread that owns the jsi::Runtime—so accessing the Runtime inside the method body is safe without extra synchronization. JSI runtimes like Hermes are single-threaded; a TurboModule method that dispatches to another thread and then touches the Runtime would be unsafe, but a direct synchronous invocation from JS stays on the calling (JS) thread.

Why:

A synchronous JSI call is a direct C++ function invocation on the calling thread. Because the call originates from JavaScript, that thread is the JS thread—the same single thread that owns the jsi::Runtime. The native method body therefore runs on the JS thread, and every jsi::Runtime API it calls (creating objects, setting properties, calling functions) is safe without locks or dispatch. Option (b) is wrong because the implementation does not get to choose the thread for a synchronous call—it is the JS thread by definition. Option (c) is wrong because JSI runtimes (Hermes, V8) are single-threaded and explicitly not thread-safe; touching the Runtime from any non-JS thread is undefined behavior. Option (d) is wrong because TurboModule synchronous calls bypass the legacy bridge's asynchronous message-queue dispatch entirely—they do not run on the native-modules thread pool. The only scenario requiring synchronization is when the native method itself spawns work on another thread and that thread later accesses the Runtime, which must be posted back to the JS thread.

React Native/core-components

You need to render a potentially very long list of user records in a React Native app. Which built-in core component is designed to keep memory low by recycling off-screen rows and only mounting items near the current viewport?

Options

  • ScrollView — it natively virtualizes children and only mounts visible rows.
  • FlatList — it recycles off-screen rows and only renders items near the viewport.
  • SectionList — it is the only list component that supports view recycling.
  • VirtualizedList is a core component you use directly in JSX for app screens.
Show answer

Use FlatList for long lists in React Native. It virtualizes its rows through an underlying VirtualizedList, recycling off-screen items so only those near the viewport stay mounted — keeping memory usage low. ScrollView renders all children at once and is unsuitable for large datasets, while VirtualizedList is the low-level primitive FlatList wraps and is not meant for direct app-screen use.

Why:

FlatList is the React Native core component purpose-built for long lists: it virtualizes its children via an underlying VirtualizedList, recycling rows that scroll off-screen so only items near the viewport are mounted. ScrollView, by contrast, renders every child at once, which causes memory and performance issues for large lists. SectionList builds on the same virtualization machinery but adds section headers — view recycling is not exclusive to it. VirtualizedList is the lower-level primitive FlatList wraps; it is not intended for direct use in typical app screens.

React Native/styling-layout

Consider the following React Native component tree: <View style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}> <View style={{ width: 40, height: 80, backgroundColor: 'red' }} /> <View style={{ width: 40, height: 20, alignSelf: 'flex-start', backgroundColor: 'blue' }} /> <View style={{ width: 40, height: 60, backgroundColor: 'green' }} /> </View> Where is the blue (middle) child vertically positioned relative to the parent container?

Options

  • Centered vertically, aligned with the red and green boxes
  • At the top edge of the parent container
  • At the bottom edge of the parent container
  • Stretched to fill the parent's full height
Show answer

The blue (middle) child is positioned at the top edge of the parent container. In a row layout the cross axis runs vertically, and while the parent's alignItems: 'center' would center all children vertically, the child's own alignSelf: 'flex-start' overrides that, snapping it to the start of the cross axis — the top edge in a row layout.

Why:

With flexDirection: 'row', the main axis is horizontal and the cross axis is vertical. The parent's alignItems: 'center' would vertically center all children, but alignSelf: 'flex-start' on the middle child overrides that, snapping it to the start of the cross axis — which in a row layout is the top edge. The red and green boxes remain vertically centered; only the blue box is pinned to the top.

React Native/lists-performance

A React Native (0.74, Hermes) screen renders a 5 000-row FlatList. Featured rows (every 50th item) are twice as tall as normal rows. The developer enables every common virtualization optimization but scrollToIndex({ index: 100 }) consistently overshoots the target and some featured rows flicker when scrolled into view. Identify the buggy line(s) in the configuration below.

import React from 'react';
import { FlatList, Text, View, StyleSheet } from 'react-native';

const data = Array.from({ length: 5000 }, (_, i) => ({
  id: String(i),
  label: `Row ${i}`,
  featured: i % 50 === 0,
}));

const MyList = () => (
  <FlatList
    data={data}
    keyExtractor={(item) => item.id}
    renderItem={({ item }) => (
      <View style={[styles.row, item.featured && styles.featuredRow]}>
        <Text>{item.label}</Text>
      </View>
    )}
    getItemLayout={(_, index) => ({
      length: 60,
      offset: 60 * index,
      index,
    })}
    initialNumToRender={8}
    maxToRenderPerBatch={8}
    windowSize={10}
    removeClippedSubviews={true}
  />
);

const styles = StyleSheet.create({
  row: { height: 60, padding: 12 },
  featuredRow: { height: 120, backgroundColor: '#fef3c7' },
});
Show answer

The bug is on lines 20 and 21.

Why:

getItemLayout is an opt-in contract that tells VirtualizedList the exact pixel position and height of every item so it can skip on-mount measurement and jump to any index in O(1). Here, styles.featuredRow (height: 120) overrides styles.row (height: 60) for every 50th item because React Native merges style arrays left-to-right, so featured rows are actually 120 px tall. Yet lines 20–21 hard-code a uniform length of 60 and compute offset as 60 * index, ignoring the extra 60 px contributed by each featured row. The cumulative offset drifts by 60 px for every featured row before the target, so scrollToIndex overshoots; the virtualization window also miscalculates which cells intersect the viewport, causing featured rows to pop in late. The fix is either to compute the real cumulative offset (summing actual heights) inside getItemLayout, or to remove getItemLayout entirely so VirtualizedList falls back to measuring each cell. removeClippedSubviews, windowSize, initialNumToRender, and maxToRenderPerBatch are all correctly configured and do not cause this symptom.

React Native/navigation

You are working with React Navigation v6/v7 (the @react-navigation/* ecosystem). Consider the following four statements about Stack and Tab navigator behavior. Select all that are true.

Options

  • navigation.navigate('Profile') will navigate back to an existing 'Profile' screen already in the current stack if one exists, rather than pushing a new instance.
  • navigation.push('Profile') always pushes a brand-new 'Profile' screen onto the stack, even if a 'Profile' screen already exists in the stack state.
  • Adding a beforeRemove event listener to a screen's navigation object lets you intercept that screen's removal (including the hardware back button on Android) and conditionally prevent it by calling e.preventDefault().
  • In a default Material Top Tab Navigator, switching from one tab to another automatically unmounts the previously focused tab's screen component to free memory.
Show answer

Statements a, b, and c are true. navigation.navigate reuses an existing screen in the stack, while navigation.push always creates a new instance. The beforeRemove event listener can intercept screen removal (including the Android back button) via e.preventDefault(). Statement d is false: Tab Navigators keep all screens mounted by default; unmounting on blur requires explicitly setting unmountOnBlur: true.

Why:

(a) TRUEnavigate searches the current stack's state for an existing screen with the matching name; if found, it pops back to that instance rather than creating a duplicate. This is the key behavioral difference from push.

(b) TRUEpush unconditionally adds a new screen instance to the top of the stack, so you can have multiple copies of the same route (e.g., a deep chain of 'Profile' screens). This is why push is used for drill-down navigation that may repeat.

(c) TRUE — The beforeRemove navigation event fires before a screen is removed from the stack. Calling e.preventDefault() on the event object cancels the removal, which is the documented pattern for prompting users about unsaved changes or confirming a back/gesture action.

(d) FALSE — Tab Navigators (including Material Top Tabs) keep all tab screens mounted by default. A screen only unmounts on blur if you explicitly set unmountOnBlur: true in that screen's options. The default preserves component state across tab switches at the cost of memory.

React Native/platform-apis

In React Native's New Architecture (TurboModules + JSI, with the bridge disabled), a native method declared isSync: true in the codegen TypeScript spec is called from JavaScript. Which statement accurately describes the dispatch and return mechanism compared to the legacy bridge? Assume both Fabric and TurboModules are enabled, and the native side implements the method in Obj-C++ / JNI.

Options

  • The JS thread serializes arguments to JSON, enqueues them on the native module dispatch queue, and blocks until the native thread posts the serialized result back—identical to the legacy bridge except the caller blocks instead of using a callback.
  • JSI registers a C++ function pointer on a JS object exposed to the engine; calling it executes native code directly on the JS thread, receives a jsi::Value (or JSValue) back without serialization, and returns synchronously to JavaScript.
  • The call still crosses an asynchronous queue boundary; isSync: true only changes the return type to a Promise that resolves on the next microtask rather than a regular callback.
  • Synchronous TurboModule methods require the Fabric renderer to be enabled; if Fabric is off, isSync: true silently degrades to legacy async bridge dispatch.
Show answer

In React Native's New Architecture, a TurboModule method marked isSync: true is invoked directly through JSI: codegen-generated C++ bindings install native function pointers on a JS object, so the JavaScript engine calls into C++ on the JS thread and receives a jsi::Value back synchronously with no JSON serialization and no queue round-trip. This replaces the legacy bridge's asynchronous message-passing model entirely. Fabric is not required for this—TurboModules and Fabric are independent.

Why:

In the New Architecture, TurboModules are backed by JSI rather than the asynchronous message queue of the legacy bridge. Codegen produces C++ bindings; when a TurboModule is instantiated, JSI creates a JS object whose methods are backed by native C++ function pointers installed via jsi::Object property accessors. A synchronous call (declared isSync: true in the spec) executes the C++ body directly on the JS thread, which then invokes the Obj-C++/JNI implementation. The return value is constructed as a jsi::Value in-place and handed back to the engine without any JSON serialization step or queue round-trip. Option (a) describes the legacy-bridge blocking model, which no longer applies once the bridge is disabled. Option (c) is wrong because isSync: true genuinely returns a plain value, not a Promise. Option (d) is wrong because TurboModules and Fabric are independent flags; synchronous methods work as long as TurboModules + JSI are enabled, regardless of whether the Fabric renderer is turned on.

React Native/core-components

You have a FlatList rendering thousands of items where each row has a fixed height of exactly 50 points. You call flatListRef.current.scrollToIndex({ index: 400 }), but the scroll lands at the wrong position or throws because FlatList has to measure each preceding row on the fly. Which single prop, when supplied, lets FlatList compute the exact offset mathematically so scrollToIndex works reliably without measuring every item?

Options

  • initialScrollIndex={n}
  • getItemLayout={(data, index) => ({ length: 50, offset: 50 * index, index })}
  • windowSize={n}
  • removeClippedSubviews={true}
  • maxToRenderPerBatch={n}
Show answer

Supply the getItemLayout prop: getItemLayout={(data, index) => ({ length: 50, offset: 50 * index, index })}. This lets FlatList compute each item's pixel offset arithmetically instead of measuring rendered rows, so scrollToIndex lands precisely and quickly even for far-down items. The other props tune rendering windows or clipping but do not provide the layout information scrollToIndex needs.

Why:

getItemLayout is a function prop that returns { length, offset, index } for any item without mounting it. When it is provided, FlatList can calculate the pixel offset for item 400 as 50 * 400 = 20000 directly, making scrollToIndex both fast and accurate. initialScrollIndex only sets the starting scroll offset on mount and itself requires getItemLayout to work correctly for large lists. windowSize controls how many items are rendered in the viewport (a memory trade-off), not layout math. removeClippedSubviews clips off-screen views but does not provide measurement data. maxToRenderPerBatch controls batch rendering rate, not offset calculation.

Related interview questions

Job market

See react-native salaries and hiring demand from live job postings.

Practice this for real

Paste the job description you're chasing and get a quiz built from what it asks. Every answer is scored on the spot, and the topics you miss come back until they stick.

What moved, monthly

One email a month when the bulletin lands: what measurably moved in the markets we track, and the new question topics we published. Confirm your address to join, and unsubscribe anytime.