React Native has become a real differentiator for frontend engineers targeting mobile-heavy product teams in India. Even when the role is labeled frontend rather than mobile, interviewers often probe whether you understand how React Native behaves under production constraints: list performance, navigation, animations, platform differences, and native-bridge trade-offs.
This guide is built for that reality. It focuses on the React Native questions and concepts Indian engineers are most likely to prepare for when interviewing with consumer, fintech, and commerce teams, along with stronger answer structures, code examples, and the reasoning interviewers are usually listening for.
Methodology note: This guide is based on common interview patterns discussed by candidates, public engineering material, role expectations, and recurring React Native fundamentals. Company references are illustrative of hiring context, not claims of official interview disclosure.
Where React Native Knowledge Shows Up in India
| Company | RN Usage | Hiring For |
|---|---|---|
| CRED | Primary mobile stack | SDE-2 RN Engineer |
| Zepto | Delivery + customer app | SDE-2 RN/Frontend |
| Meesho | Buyer + seller apps | SDE-1, SDE-2 |
| PhonePe | UPI + payments | Frontend (RN preferred) |
| Swiggy | Delivery partner app | Frontend (RN a plus) |
| Flipkart | Seller hub | UI Engineer 2 |
If you already know React well, the hard part is not learning a completely new mental model. The hard part is understanding what changes once you leave the browser: threading, gesture handling, navigation state, offline behavior, and the performance cost of careless rendering.
The Architecture Questions
Q1: How does React Native work? Explain the bridge.
This is one of the most common React Native fundamentals questions in interviews. A good answer should explain the old model clearly, identify the bottleneck, and then explain what changed with JSI, Fabric, and TurboModules in practical terms.
The old architecture (pre-0.71, what most Indian apps still run):
JavaScript Thread Bridge (Async) Native Thread
───────────────── ───────────── ──────────────
React code runs here → Serialized JSON → Native UI rendered
↑
useState, useEffect ← JSON responses ← User interactions
The bridge is the bottleneck:
- All communication is asynchronous — JS to Native and back
- Messages are serialized to JSON — large objects slow things down
- Animations that cross the bridge (JS → Native → JS → Native) can drop frames
- Layout callbacks that trigger re-renders can cause jank
Real consequence: This is why React Native animations traditionally need Animated API or Reanimated 2 — they move animation calculations to the native thread to avoid bridge overhead.
The new architecture (JSI — JavaScript Interface):
JavaScript Thread JSI (Synchronous) Native Thread
───────────────── ───────────────── ──────────────
React code runs here → Direct function calls → Native modules
↑ no serialization
↑ synchronous (no queue)
↑ C++ binding
JSI advantages:
- Native modules called synchronously — no JSON serialization
- Fabric: new concurrent rendering for Native UI
- TurboModules: lazy-loaded native modules (faster startup)
What to say in an interview:
"The old architecture uses an asynchronous bridge where all JS-to-native communication is serialized to JSON. This creates performance bottlenecks — especially for animations that need to cross the bridge 60 times per second. The new architecture replaces this with JSI, a C++ binding that allows synchronous direct calls between JS and native code, eliminating serialization overhead."
Q2: What is the difference between FlatList and ScrollView?
// ScrollView — renders ALL children immediately
<ScrollView>
{thousandItems.map(item => <Item key={item.id} {...item} />)}
</ScrollView>
// Problem: 1000 items = 1000 components mounted at once
// Memory usage: HIGH | Initial render: SLOW
// FlatList — renders only VISIBLE items (virtualized)
<FlatList
data={thousandItems}
renderItem={({ item }) => <Item {...item} />}
keyExtractor={item => item.id}
// Only ~20 items in DOM at any time (window size)
/>
// Memory usage: LOW | Initial render: FAST
// Items are mounted/unmounted as user scrolls
When to use ScrollView:
- Small, fixed lists (under 20–30 items)
- Heterogeneous content (different component types mixed — like a profile page)
- When you need all content loaded for measurements
When to use FlatList:
- Any list with dynamic/unknown length
- Feeds, search results, transaction history
- Any list where performance matters
Common FlatList optimizations interviewers ask about:
<FlatList
data={items}
renderItem={renderItem}
keyExtractor={item => item.id}
// Performance props:
initialNumToRender={10} // Only render 10 items initially
maxToRenderPerBatch={10} // Render 10 per scroll batch
windowSize={5} // Keep 5 screen-heights of items in memory
removeClippedSubviews={true} // Unmount items far off screen (Android)
// Avoid re-renders:
getItemLayout={(data, index) => ({ // Pre-calculate item sizes
length: 80,
offset: 80 * index,
index,
})}
/>
Q3: How do you handle navigation in React Native?
React Navigation is the default answer for most React Native interviews because it is widely used and easy for interviewers to discuss at the architecture level.
// Stack Navigator — screen pushes (most common)
const Stack = createNativeStackNavigator();
function AppNavigator() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="JobDetail" component={JobDetailScreen} />
<Stack.Screen name="Apply" component={ApplyScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
// Navigate programmatically
function HomeScreen({ navigation }) {
return (
<Button
title="View Job"
onPress={() => navigation.navigate('JobDetail', { jobId: '123' })}
/>
);
}
// Receive params
function JobDetailScreen({ route }) {
const { jobId } = route.params;
// fetch job by jobId...
}
Tab Navigator (bottom tabs — very common in Indian apps):
const Tab = createBottomTabNavigator();
function TabNavigator() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStack} />
<Tab.Screen name="Search" component={SearchStack} />
<Tab.Screen name="Profile" component={ProfileStack} />
</Tab.Navigator>
);
}
// Each tab has its own stack navigator — nested navigation
Interview question: "How do you deep link to a specific screen?"
// Deep link config in NavigationContainer
const linking = {
prefixes: ['myapp://', 'https://myapp.com'],
config: {
screens: {
Home: '',
JobDetail: 'jobs/:jobId', // myapp://jobs/123
Apply: 'jobs/:jobId/apply', // myapp://jobs/123/apply
},
},
};
<NavigationContainer linking={linking}>
{/* ... */}
</NavigationContainer>
Q4: Explain React Native Animations. Animated API vs Reanimated 2.
Animated API (built-in, runs on JS thread):
import { Animated } from 'react-native';
function FadeIn({ children }) {
const opacity = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(opacity, {
toValue: 1,
duration: 300,
useNativeDriver: true, // IMPORTANT: offloads to native thread
}).start();
}, []);
return (
<Animated.View style={{ opacity }}>
{children}
</Animated.View>
);
}
Critical: useNativeDriver: true
With useNativeDriver: false (or missing):
- Animation calculations happen on JS thread
- JS thread also runs React reconciliation, event handlers, API calls
- Result: animation drops frames when JS is busy
With useNativeDriver: true:
- Animation runs on UI thread (separate from JS)
- JS thread can be completely blocked — animation still runs at 60fps
- Limitation: only works for
opacity,transform— NOTwidth,height,padding
Reanimated 2 (recommended for complex animations):
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
withSpring,
} from 'react-native-reanimated';
function SpringCard() {
const scale = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));
return (
<Animated.View style={[styles.card, animatedStyle]}>
<Pressable
onPressIn={() => { scale.value = withSpring(0.95); }}
onPressOut={() => { scale.value = withSpring(1); }}
>
<Text>Press Me</Text>
</Pressable>
</Animated.View>
);
}
Why Reanimated 2 is better:
- Animations run entirely on UI thread — not JS thread at all
- Gesture handler integration is seamless (Gesture Handler + Reanimated = standard stack)
- More expressive API —
withSpring,withDecay,withSequence
When Indian companies ask this: "CRED's UI is known for being heavily animated. They'll ask this at senior level — know both APIs."
Q5: How do you optimize FlatList performance for large lists?
This is asked at Zepto (order history), Meesho (product listings), Swiggy (restaurant list).
// Full optimization example
const MemoizedItem = React.memo(({ item, onPress }) => (
<TouchableOpacity onPress={() => onPress(item.id)}>
<Text>{item.title}</Text>
</TouchableOpacity>
));
function OptimizedList({ data }) {
// 1. Memoize render function — prevents re-creates per render
const renderItem = useCallback(({ item }) => (
<MemoizedItem item={item} onPress={handlePress} />
), [handlePress]);
// 2. Memoize key extractor
const keyExtractor = useCallback(item => item.id, []);
// 3. Pre-calculate item sizes (eliminates layout measurement)
const getItemLayout = useCallback((data, index) => ({
length: ITEM_HEIGHT, // Fixed height
offset: ITEM_HEIGHT * index,
index,
}), []);
// 4. Stable handler
const handlePress = useCallback((id) => {
navigation.navigate('Detail', { id });
}, [navigation]);
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout} // Only if fixed heights
// Window management
initialNumToRender={10}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={50}
windowSize={10}
removeClippedSubviews={true} // Android only
// Empty state
ListEmptyComponent={<EmptyState />}
// Loading more
onEndReached={loadMore}
onEndReachedThreshold={0.5} // Load more when 50% from bottom
ListFooterComponent={isLoading ? <Spinner /> : null}
/>
);
}
Q6: What is the difference between StyleSheet.create and inline styles?
// Inline styles — new object created on every render
function BadComponent() {
return (
<View style={{ flex: 1, backgroundColor: 'white', padding: 16 }}>
{/* New style object every render = more GC pressure */}
</View>
);
}
// StyleSheet.create — styles are created once, registered with native
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
padding: 16,
},
});
function GoodComponent() {
return <View style={styles.container}>{/* ... */}</View>;
// style reference is stable — no new object per render
}
Why StyleSheet.create is better:
- Styles are validated at creation time (typos caught early)
- Registered with native layer — sent over bridge once, referenced by ID
- No new object allocation on every render
- Enables style optimization in the native layer
Q7: How does React Native handle platform differences?
import { Platform } from 'react-native';
// Platform-specific code
const styles = StyleSheet.create({
container: {
paddingTop: Platform.OS === 'ios' ? 44 : 24, // iOS has notch
...Platform.select({
ios: { shadowColor: '#000', shadowOpacity: 0.1 },
android: { elevation: 4 },
}),
},
});
// Platform-specific components
const Button = Platform.select({
ios: () => require('./IOSButton'),
android: () => require('./AndroidButton'),
})();
// Platform-specific file names (automatic)
// Button.ios.js ← used on iOS
// Button.android.js ← used on Android
// Button.js ← fallback for both
Q8: How do you handle offline support in React Native?
Asked at Meesho (Tier-2 India has spotty internet), PhonePe (payment must work offline):
import NetInfo from '@react-native-community/netinfo';
import AsyncStorage from '@react-native-async-storage/async-storage';
// 1. Detect connectivity
function useNetworkStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
setIsOnline(state.isConnected && state.isInternetReachable);
});
return unsubscribe;
}, []);
return isOnline;
}
// 2. Cache data locally
async function cacheJobListings(jobs) {
await AsyncStorage.setItem(
'cached_jobs',
JSON.stringify({ data: jobs, timestamp: Date.now() })
);
}
async function getCachedJobs() {
const cached = await AsyncStorage.getItem('cached_jobs');
if (!cached) return null;
const { data, timestamp } = JSON.parse(cached);
// Stale after 1 hour
if (Date.now() - timestamp > 3600000) return null;
return data;
}
// 3. Queue offline actions (payments, form submissions)
async function queueOfflineAction(action) {
const queue = JSON.parse(await AsyncStorage.getItem('offline_queue') || '[]');
queue.push({ ...action, timestamp: Date.now() });
await AsyncStorage.setItem('offline_queue', JSON.stringify(queue));
}
// 4. Sync when back online
useEffect(() => {
if (isOnline) processOfflineQueue();
}, [isOnline]);
Q9: What are common performance issues in React Native?
| Issue | Symptom | Fix |
|---|---|---|
| JS thread blocked | UI freezes during computation | Move to InteractionManager or native module |
| Bridge congestion | Animations jank when scrolling | useNativeDriver: true or Reanimated 2 |
| Too many re-renders | List scrolls slowly | React.memo + useCallback + FlatList optimization |
| Large bundle | Slow app startup | Code splitting, Hermes engine, RAM bundles |
| Image memory leaks | App crashes on image-heavy screens | FastImage, proper sizing, list recycle |
| Memory leak in effects | App gets slower over time | Cleanup functions in useEffect |
Q10: New React Native Architecture — What Changed?
If you're interviewing for Senior/Lead:
Old Architecture: New Architecture (0.71+):
────────────────── ─────────────────────────
JS Thread JS Thread (Hermes engine)
↓ async ↓ direct C++ binding
Bridge JSI (JavaScript Interface)
↓ serialize ↓ synchronous, no serialization
Native Thread Fabric (new renderer)
TurboModules (lazy native modules)
Codegen (type-safe native bridge)
Real-world impact:
- App startup is 30–40% faster (TurboModules loaded lazily)
- Animations smoother (Fabric renders concurrently with React 18)
- Native modules can be called synchronously (unblocks entire class of bugs)
- React 18 concurrent features work natively (Suspense, transitions)
Cheat Sheet: Common React Native Focus Areas in Interviews
Consumer apps: Reanimated, gesture handling, list performance
Commerce apps: FlatList optimization, offline support, image memory
Regional apps: Platform differences, localization, layout consistency
Fintech apps: Navigation, deep linking, persistence, security
Realtime apps: WebSocket updates, background behavior, battery impact
Browse React Native and frontend jobs in India on OnlyFrontendJobs if you're targeting product teams that value mobile experience.
