React Native · 14 min
React Native interview questions and answers
Twelve React Native interview questions on rendering, lists, effects, the New Architecture, Hermes and storage, with what is scored, the trap and the follow-up.
React Native · 13 min read
Ten senior React Native interview questions on migration, startup, retries, old app versions, feature flags and native rewrites, with the follow-up to each.
This page is for React Native engineers interviewing at senior level, or at mid level for a team that asks senior questions. You get ten senior React Native interview questions, what each one scores, the answer that sounds senior but is not, the one that is, and the follow-up that decides it. From the hiring side, the difference is rarely knowledge of an API. A mid-level answer describes the implementation. A senior answer starts with the constraint, picks a trade-off out loud, says how it will be measured, and says how it will be rolled out and rolled back.
| Step | What it sounds like |
|---|---|
| Constraint | “Which app versions are still in use, and what is the release cadence?” |
| Decision | The recommendation, in one or two sentences |
| Trade-off | One real alternative, and what would make you switch |
| Measure | The metric, the baseline and the device class |
| Rollout | Who gets it first, and what triggers a rollback |
If your answer ends after the second row, it is the mid-level answer, whatever your title is. The implementation is one part of a senior answer, not the whole of it.
What is scored: an inventory, a sequence and a way back.
Weak answer: “Upgrade React Native, turn it on and fix what breaks.”
Senior answer: “Since 0.82 the New Architecture is the only one you can run, so this is a compatibility job with a deadline, not a performance experiment. I start with an inventory of every native dependency and every custom native module or view, and for each one I record whether it supports Fabric and TurboModules, who maintains it, and what replaces it if it is abandoned. The least maintained ones go first, because they take longest to patch or replace. Custom modules move to TurboModules with a typed spec. Then I validate on a device matrix that matches our users, ship to an internal group, then a small share of production, with a rollback path until the telemetry says it is stable.”
The follow-up: “The library compiles. Is it ready?” A clean build is where the check starts. I would exercise its native views, events, threading and background behaviour on real devices. A library that only works through leftover compatibility code carries upgrade risk, because later releases keep removing that code.
Senior answer: “Crash rate and ANR or freeze rate, split by architecture cohort. Startup and key interaction times, before and after. Errors from native modules. And completion rates for the flows that make money, like checkout or sign-up. Every number is sliced by release and device tier, which lets you blame a regression on the migration and not on an unrelated change that shipped in the same week.”
The trap: starting to look closely only after the full rollout. The baseline has to exist before the first user gets the new build. Without it, “before and after” is two numbers from different weeks and different traffic.
What is scored: a target before the work, and a measurement after it.
Senior answer: “I weigh the measured gain, how much faster, for which share of users, on which interaction, against what it costs to keep: harder code, more edge cases, a larger place for bugs to hide. I want a stated target before anyone starts, and a re-measurement on the same device and build afterwards. It is very easy to believe a complex change must have helped without ever checking.”
The follow-up: “It is 40 ms faster on the iPhone in the office.” Then we do not know yet. Measure it on the device class where users complain and in a release build before it is worth the complexity.
Senior answer: “Instrument before optimising. I mark the startup milestones: process start, JavaScript ready, first screen rendered, first screen usable. Then I shrink what runs before the first interactive screen: synchronous work at startup, native modules and SDKs that are initialised eagerly but not needed yet, analytics that can wait for the first interaction, and a first screen that waits for more data than it needs to show something useful. TurboModules load lazily, which helps only if the app does not touch them all at startup anyway.”
The trap: optimising the part you suspect. Without milestones you can spend a sprint on the bundle when the real delay is an SDK call on the main thread.
What is scored: telling retryable failures from unsafe ones, and knowing the server has to take part.
Senior answer: “I retry transient failures, timeouts and some 5xx responses, with exponential backoff, jitter and a cap on attempts. I do not retry validation errors, and I do not retry a mutation unless it is idempotent. For mutations the client generates one key per logical action and sends the same key on every attempt, so an automatic retry, a user tapping twice and a replayed offline queue all look like one action to the server. The server has to recognise that key and return the first result. That is a contract with the backend team. A key the server ignores protects nothing.”
The follow-up: “The payment request timed out. Retry?” Only with the same idempotency key, and only because the server deduplicates on it. Otherwise a timeout is an unknown outcome, and the honest move is to ask the server what happened before charging again.
Senior answer: “Mobile clients do not all upgrade, so the backend has to keep serving a contract that old versions understand for as long as they have meaningful usage. That needs the client version on every request, a dashboard of active versions, and a written policy for how long a version is supported. A forced update is sometimes right, for a security fix, but it has to be a deliberate mechanism the team decides to use, announced ahead where possible, not the side effect of a backend change nobody coordinated.”
The trap: “We just force everyone to update.” Then every backend change becomes a release-day emergency, and the users who cannot update that day see a broken app with no explanation.
Senior answer: “Two things. Flags that are never removed multiply the paths the code can take, and nobody tests the combinations. And a kill switch that has never been flipped gives false confidence: the first time anyone uses it is during an incident, which is the worst time to learn it does not work. So every flag has an owner and an expected end date, rollout flags are deleted when the rollout finishes, and the kill switches on critical paths are exercised on purpose now and then.”
The follow-up: “How many flags is too many?” There is no number. The question is whether each one has an owner and a date, and whether the team knows which combinations run in production.
What is scored: measured need against the cost of owning native code.
Senior answer: “Native when the feature needs something JavaScript cannot reasonably do: a platform API with no good wrapper, deep hardware integration, or work that is measured to be too slow in JavaScript. Against that I put the cost: two implementations to maintain, people on the team who can maintain them, and a bigger surface to break on every React Native and OS upgrade. Before writing custom native code I check whether a well-maintained library already does it. With Hermes and the New Architecture, JavaScript covers more cases than it did a few years ago, so ‘it is performance-sensitive’ is not enough on its own.”
The trap: going native for anything that sounds fast. You pay for that module on every upgrade, for years.
Senior answer: “When one of three signals holds, and it has been measured, not felt. A performance ceiling that React Native cannot reach after the known optimisations are done and profiled. A flow that depends on a platform API so continuously that the wrapper adds more fragility than value. Or interaction patterns so platform-specific that one shared implementation keeps fighting both platforms’ conventions. A screen that feels slow and has not been profiled is not evidence. It is a to-do list.”
The follow-up: “Product wants it by next quarter.” Then the rewrite has to compete with the fixes that have not been tried yet. Put the measured gap, the cost of a second implementation, and the owner in the same document, and let that decide.
Senior answer: “One owner for the migration, even though the work sits in many teams. A written sequence with dates, a compatibility list that is kept current, and one shared measurement so each squad reports the same numbers. Each squad migrates its own screens, the platform team unblocks shared dependencies first, and nothing reaches all users before the canary cohort has been stable for an agreed period. The hard part is not technical. It is keeping the priority alive for two quarters while product work competes with it.”
The trap: a big-bang cutover. All the problems land in one week, exactly when the app has the least slack to cope with them.
Prompt: “The product detail screen is slow on Android. A senior engineer wants to rewrite it in Kotlin and Swift. What do you say?”
Strong answer: “I would not decide yet. First: slow how? Time to content, scroll, or taps that respond late. I would reproduce it in a release build on the device class that complains, record JS and UI thread frame rates, and profile renders. On a screen like this I usually find a large image decoded at full size, a carousel re-rendering on every scroll event, or a first render waiting for four requests. Those are fixes measured in days, not a rewrite measured in quarters.”
Push: “We fixed those. The gallery gesture still drops frames.” Now it is worth asking where the gesture runs. If it is handled in JavaScript, moving it to the UI thread with a native gesture library is a targeted native step. It keeps the screen in React Native and removes the measured bottleneck.
Push: “It only stays inside the frame budget on new phones.” Then there is evidence for a native component, scoped to the gallery, not the screen. Write down the measured gap, the second implementation it creates, who owns it, and the condition under which you would remove it again.
Push: “Who maintains it?” If the answer is nobody on the team who writes Kotlin and Swift, the decision changes. That is the cost the original proposal left out.
| Push | Example | A strong answer |
|---|---|---|
| Add a cohort | “Only users on version 4.2 see it.” | Checks the contract that version expects |
| Break it | “The rollout doubled the crash rate for 1% of users.” | Names the rollback trigger and the flag |
| Retry it | “The request timed out halfway.” | Idempotency key, and asking the server what happened |
| Cost it | “What does the native module cost next year?” | Upgrades, two codebases, the owner |
| Scale it | “Five squads have to do this.” | One owner, one sequence, one shared metric |
Answer each question aloud, then answer the follow-up without reading it. Where your answer describes the implementation but not the rollout, the metric or the way back, that is the one to rework. The broader question set, with rendering, lists and the New Architecture, is on React Native interview questions and answers. If the loop has an architecture round, read mobile architecture interview questions as well.
newArchEnabled=false on Android and RCT_NEW_ARCH_ENABLED=0 on iOS are ignored (React Native 0.82).
React Native · 14 min
Twelve React Native interview questions on rendering, lists, effects, the New Architecture, Hermes and storage, with what is scored, the trap and the follow-up.

Senior and Staff loops · 11 min
Architecture interview questions that do not depend on the platform: modules, shared code, server-driven UI, flags, old app versions and who owns what.

Senior and Staff loops · 15 min
How I decide Staff vs Senior in a mobile system design loop: the questions I ask, what each level answers, the follow-ups, and how down-levelling happens.