Ideas Engineered for Tomorrow
We Engineer Services & Solutions for Your Business Needs
Consulting Services Hire Book Consulting

Hire React Native Developers in India

There are two kinds of candidate behind that job title, and they cost you very different amounts. This page is about telling them apart before you sign anything.

Jump to Screening

When you set out to hire React Native developers in India, the resumes will look interchangeable. React, TypeScript, Redux or Zustand, a few App Store links, some years. Underneath that sameness sits the only question that matters: are you buying a JavaScript developer who ships to phones, or a mobile engineer who happens to write JavaScript?

The first one is genuinely useful and will build most of your product. They know React deeply, they compose components well, they will get your screens on both platforms faster than two native teams would. Then a Bluetooth SDK arrives with an Objective-C header and no JavaScript wrapper. Or a customer on a three year old Android phone reports a crash nobody can reproduce. Or the feed starts dropping frames the moment the images load. At that point the first kind of developer stops, and the ticket sits in the sprint moving right, week after week, while everyone waits for it to somehow resolve itself.

The second kind opens Xcode. They read the Kotlin. They write the bridging code, symbolicate the stack trace against the mapping file from that build, and come back with an answer. They are much rarer, they are worth considerably more, and almost nothing in a standard interview process distinguishes them from the first kind. So the rest of this page is about that distinction: what the framework asks of a person, where the JavaScript ends and the platform starts, and the specific questions that surface the difference in about an hour.

What Does a React Native Developer Do That a React Developer Does Not?

Roughly two thirds of the work is React you would recognise. The other third is where hires fail, and it is the third nobody interviews for.

The familiar two thirds

Components, hooks, props, state, data fetching, form handling, list rendering, TypeScript types over an API contract. If you already have a React web team, this part transfers almost directly, which is the honest reason React Native keeps winning arguments in companies that already run React on the web. The mental model survives the move to phones.

No DOM, no CSS, no browser

There is no div. There is a View, which becomes a UIView on iOS and an android.view.View on Android. There is no cascade, no media queries, no percentage tricks that quietly rely on the layout engine. Styles are a subset of flexbox implemented by Yoga, and the differences bite in small ways every day: no gap in older versions, no shadow property that behaves identically on both platforms, no text overflow that works the way you expect.

Two build systems you now own

CocoaPods and a Podfile.lock on one side. Gradle, the Android Gradle Plugin and a Kotlin version that has to agree with everything else on the other. A React developer has never had a build fail because two transitive dependencies wanted different versions of a native library. A React Native developer has that happen on a Tuesday and needs to know where to look.

Releases you cannot undo

A bad web deploy is reverted in ten minutes. A bad binary sits on people's phones until the next one clears review, and the reviews you earn in the meantime do not get deleted afterwards. This changes what care means. Defensive decoding, feature flags, staged rollout and a crash dashboard somebody actually reads stop being nice practices and start being the job.

Permissions, backgrounding and the OS

Notification permission has to be asked for on modern Android. Background work is subject to Doze, to app standby buckets, and to whatever the manufacturer added on top. Location has foreground and background variants with separate review consequences. None of this exists in a browser tab, and all of it turns up in your bug tracker as reports that cannot be reproduced on the developer's own phone.

The JavaScript and native seam

Every meaningful capability your app has, from the camera to secure storage to push, is native code with a JavaScript face on it. Someone has to understand both sides of that seam when it leaks, and it leaks most often in release builds on hardware that nobody on the team owns.

The New Architecture: Fabric, TurboModules and JSI

If you only learn one piece of React Native internals before interviewing, make it this one. It explains most of the framework's historical reputation for jank, it is the single largest change the project has ever made, and where a codebase sits relative to it tells you an enormous amount about the team that built it and the work ahead of you.

What the old bridge was, and why it hurt

For most of React Native's life, JavaScript and native code could not call each other directly. They exchanged serialised messages over an asynchronous queue known as the bridge. Every message was turned into JSON, put in a batch, sent across, parsed on the other side and acted on. It worked, and for ordinary screens it worked well enough that nobody cared.

It failed in two specific places, and both are places users notice. First, anything high frequency saturated the queue. Scroll events, gesture updates and animation frames produced a flood of messages, and once the queue backed up your interface fell behind the finger on the glass. Second, nothing could be synchronous. JavaScript could not ask native for a measurement and use the answer in the same frame, so patterns that needed a layout value before drawing had to guess, draw twice, or flicker.

What JSI changed

The JavaScript Interface is a thin C++ layer that lets JavaScript hold a direct reference to a native object and invoke methods on it, synchronously, with no serialisation in between. That one change removes the queue as a bottleneck and makes everything else in the New Architecture possible. It also decouples the framework from any single JavaScript engine, which is how Hermes became viable as the default.

Fabric, and why rendering moved to C++

Fabric is the rendering system built on JSI. The shadow tree that decides where every view goes now lives in C++ and is shared between threads, which means layout can be computed synchronously when it needs to be, and the tree is immutable so React can work on a new version while the old one is still on screen. That last property is what makes React Native compatible with concurrent React rather than perpetually a version behind it.

TurboModules and codegen

Under the old system, every native module registered itself at app launch whether or not you ever used it. TurboModules load on first use instead, which cuts startup work in an app carrying thirty dependencies. The other half is codegen: you declare the module's interface once in a TypeScript spec, and the build generates the C++ and platform glue from it. The practical effect is that a mismatch between what JavaScript expects and what native provides becomes a build error rather than a crash in production.

What an old-bridge codebase tells you about the hire

The New Architecture became the default for new apps in the 0.76 release. There is an interoperability layer so legacy modules can keep working, and plenty of shipping apps still run the old path perfectly happily. So finding it is not a scandal. What it tells you is where the maintenance debt sits.

Someone stopped upgrading

An app several majors behind usually stalled for a reason, and the reason is normally a dependency that never migrated. Ask which one. If nobody knows, that is the first week of the engagement right there, and it is not feature work.

Performance fixes may be structural

Some of the jank you are living with is the queue, not your code. Rewriting screens will not fix it. Knowing which is which before you commit a quarter to optimisation is worth an afternoon of investigation.

The animation libraries moved on

Recent majors of the main animation and gesture libraries target the New Architecture, so staying on the old path gradually pins you to older versions of everything. Check the support matrix of what you depend on before planning the upgrade, because it decides the order.

Expo or Bare: Which One Are You Actually Hiring For?

This question used to be a fork in the road. It no longer is, but a lot of developers still answer it as though it were, and how they answer tells you when they last shipped something.

What Expo gives you

A managed toolchain over React Native: a fixed set of native modules that are known to work together, cloud builds so nobody needs a Mac to produce an iOS binary, a submission pipeline, push infrastructure, and over the air updates. For a small team it removes an entire category of work, most of it work that produces no product value. The counterargument used to be that you were trapped inside the SDK.

Prebuild and config plugins, which killed the old argument

Prebuild generates the ios and android directories from your configuration each time rather than keeping them checked in, and config plugins let a library declare the native changes it needs, so adding something outside the SDK no longer means abandoning the tooling. This is the part older developers frequently do not know. If a candidate tells you flatly that Expo cannot handle custom native code, they have not looked in a couple of years, and that is a data point about how they keep current.

Where the managed path genuinely stops

It stops when you need a native dependency that will not express itself as a config plugin, when a vendor ships a binary SDK with its own build requirements, when you need to modify the native project in ways generation cannot express, or when your organisation will not put signing credentials in a cloud build service. Those are real reasons. Vague discomfort is not one.

What bare actually costs, every year

Owning the native projects means owning them at upgrade time. Every React Native upgrade becomes a manual diff against two directories full of files you did not write and do not fully understand. Every Xcode release brings a signing or deployment target change. Someone maintains the CI machines, the certificates, the Gradle config. None of that is hard. All of it is a tax, paid monthly, by the most expensive person on the team.

The honest recommendation

Start managed. Move to a development build the moment you need native code the SDK does not carry, which for many products is month three and for some is never. The rule we apply internally is that you should be able to justify the bare workflow with a named requirement, not with a feeling about control. If the requirement is real, take it, and price the maintenance in openly.

When You Have to Drop to Native, and What That Means for Hiring

This is the pressure test on the whole hire. Everything above the seam is JavaScript and most candidates handle it. Below the seam is Swift, Kotlin, occasionally Objective-C and C++, and the population of people who are comfortable there while also being fluent in React is small.

The situations that force it

A hardware vendor gives you an SDK with no JavaScript wrapper: payment terminals, medical devices, industrial scanners, in-car units. A capability has no maintained community module, or the maintained one was abandoned two years ago. You need work to continue when the app is backgrounded in ways the OS treats specially. You need a view that draws thousands of items at sixty frames per second, and the bookkeeping cost of doing it through React is the problem. Or a library exists, mostly works, and has one bug between you and shipping.

What writing a native module involves

On the New Architecture, you declare the interface in a TypeScript spec, run codegen, and implement the generated protocol on each platform. On iOS that is Swift or Objective-C plus a header the compiler will be unsympathetic about. On Android it is Kotlin, plus the Gradle work to make the dependency resolve. Then the awkward parts: threading, because you have to be deliberate about which thread the native work runs on and which one touches the view hierarchy; memory, because you now own object lifetimes on the iOS side; and error propagation, because a native exception that reaches JavaScript unstructured is useless in a crash report. The Expo Modules API makes a lot of this pleasanter with a Swift and Kotlin DSL, and I would reach for it before hand-writing the old style.

Patching someone else's module, which is more common

The realistic version is not a greenfield module. It is a community library with an open issue, a stale pull request and no maintainer. Someone has to read the Kotlin, find the wrong assumption, fix it, hold the fix in place with a patch tool or a fork, and carry it forward through upgrades. That is unglamorous work and it decides schedules. Ask a candidate whether they have done it. The answer is very hard to fake, because the story comes with details: the library name, the symptom, what the fix was, and what happened to the patch afterwards.

What this means for who you hire

You do not need every engineer on the team to write native code. You need at least one who can, and you need to know which one before the ticket arrives rather than after. A team of three strong React developers and nobody who opens Xcode will run beautifully until the first native problem, and then run at zero. When we put a React Native pod together, native capability is an explicit slot, not a hope.

Lists, Re-renders and the Performance Work Nobody Budgets For

Ask any experienced React Native engineer where the performance bugs live and you will get the same answer with different nouns: the list. Feeds, chat threads, order histories, product grids, search results. Whatever your app is, its list is where the frames go missing, and it is the single most reliable interview topic in this stack.

Why the list is always the problem

Everything else on screen is bounded. A list is not. It has an unknown number of rows, each row has images that decode on some thread, each row has its own state, and the user scrolls it faster than anything can prepare. Add pull to refresh, infinite loading, a sticky header and rows of varying height, and you have most of the ways a mobile app can stutter in one component.

FlatList, virtualisation and the settings that matter

FlatList only keeps a window of rows mounted and drops the rest. That works if you help it. Give it a stable keyExtractor, not the array index, or every insertion invalidates the world. Give it getItemLayout when rows have a known height, so it can position without measuring. Tune initialNumToRender to what actually fits on screen rather than the default. Understand windowSize as a multiplier on the viewport, not a row count. Most of the badly performing lists I have looked at were using defaults and rendering a large component per row with no memoisation, which is exactly the combination that produces blank cells during a fast scroll.

FlashList and view recycling

Shopify's FlashList takes a different approach, recycling row views the way native list components do instead of mounting and unmounting them. On heavy lists the difference is visible rather than theoretical. It also changes how you write rows, because a recycled row must not assume it starts clean, and a candidate who has used it will tell you about exactly that trap. Note that the two major versions behave differently, including whether you need to supply a size estimate, so check which one a codebase is on before assuming anything about it.

Re-render cost, which is where the real waste is

Rows that re-render on every parent state change are the quiet killer. Common causes: a context holding an object rebuilt each render so every consumer wakes up, a store selector returning a fresh object literal, an inline arrow passed as a prop, a style object created in the body of the component. None of these are exotic and all of them are ordinary React mistakes. The difference is that on a phone, with a JavaScript engine that has less headroom than a desktop browser, they show up as a visible stutter rather than as nothing at all.

Animations that survive a busy thread

The reason Reanimated exists is that animation driven from the JavaScript thread stops when the JavaScript thread is busy, and the JavaScript thread is always busy at exactly the moment the animation matters. Worklets run on the UI thread instead, so a gesture stays attached to the finger while data is being parsed elsewhere. Pair it with the gesture handler library so touches are recognised natively rather than being fed through JavaScript. A candidate who reaches for the old Animated API with useNativeDriver and knows precisely which properties that supports is fine. One who cannot explain why a spinner freezes during a large state update is not ready for a performance ticket.

Startup, and what Hermes is for

Hermes is the JavaScript engine React Native ships with, and its point is startup. It compiles to bytecode at build time rather than parsing your bundle on the user's device at launch, which matters most on the cheap Android hardware most of your users are actually holding. If someone tells you cold start is slow, the first questions are the bundle size, what runs before the first screen paints, and how many native modules initialise eagerly. The last one is the argument for TurboModules restated as a number.

Navigation, and Why It Is a Hiring Signal

Navigation looks like plumbing. It is actually the place where an app either feels like it belongs on the phone or feels like a website in a frame, and the difference is technical rather than aesthetic.

Native stack versus the JavaScript one

React Navigation can drive transitions in JavaScript or hand them to the platform. The native stack option delegates to the real navigation containers underneath, which is why it gets the correct swipe back gesture on iOS, the correct system back behaviour on Android, and transition timing that matches every other app on the device. Someone who has only used the JavaScript stack usually does not know why their back gesture feels slightly wrong, and cannot fix it, because the fix is not in their code.

Deep links, and the part teams always underestimate

Opening the app from a link sounds trivial and is not. On iOS, universal links require a site association file served from your domain with the right content type and no redirect. On Android, app links require a digital asset links file and a signing fingerprint that has to match the build you actually shipped, which is a favourite way to have links work in debug and fail in production. Then the app side: the link has to resolve to a screen, that screen usually needs a back stack behind it rather than being stranded, and the app may be cold, warm or already showing something else when the link arrives. Each of those is a separate code path.

Expo Router and file based routing

Expo Router puts routes on the filesystem, which will feel immediately familiar to anyone who has worked in Next.js, and it wires up deep linking and typed routes as a consequence of the file layout rather than as a separate configuration exercise. It sits on React Navigation, so the underlying concepts still apply. Worth knowing that this is a structural decision, not a library preference, because moving an established app onto it is a refactor of every screen file.

State restoration, which is the bug you will get

Android will kill your process in the background and then restore the app when the user returns, expecting to find them where they left off. If navigation state was only ever held in memory, they land on the home screen instead and lose whatever they were doing. This is reported as a vague complaint about the app resetting itself, it is nearly impossible to reproduce on a developer machine, and it is a genuinely good interview question because the people who have hit it never forget it.

Over the Air Updates: What the Stores Actually Permit

This is React Native's most commercially interesting property and the one most often misunderstood in both directions. Some teams believe they can ship anything without review. Others believe the whole idea violates store policy. Both are wrong.

How it works

Your binary contains a JavaScript bundle. On launch, the app can check a server, find a newer bundle built from the same native foundation, download it and use it on the next start. The native code, the permissions and the SDK set are unchanged, because they are baked into the binary. Only the JavaScript moves. Expo's update service is the mainstream managed way to do this now; self-hosting an update server is the alternative for teams who want the artefacts inside their own infrastructure.

What the stores allow

Apple's position, broadly, is that code interpreted by a runtime already shipped inside your reviewed app is acceptable so long as it does not change what the app fundamentally is or add features outside what was reviewed. Google's policy is similarly concerned with apps that fetch and execute code to become something other than what was published. The practical line is the same on both: fix and refine what you shipped, do not smuggle in a new product. The wording is revised periodically and the consequences of getting it wrong land on your account rather than ours, so read the current guidelines and, if the answer matters commercially, take it to your own counsel rather than to a blog post.

What it can and cannot carry

The change you want to ship Route
Wrong copy, wrong currency symbol, broken validation ruleOver the air, minutes
Logic bug crashing a screen, bad API handlingOver the air, minutes
Layout and styling changes, a redesigned screenOver the air, minutes
A feature built only from existing native capabilityOver the air, with care about scope
Adding a native library, camera, biometrics, a new SDKNew binary, store review
A new permission or entitlementNew binary, store review
React Native version changeNew binary, store review
App icon, name, store listingNew binary or store console

The discipline it demands

Update capability without process is a way to break production faster. A bundle must be matched to a compatible binary, because pushing JavaScript that calls a native module the installed binary does not contain crashes on launch for everyone who takes it. So you need release channels, a runtime version that gates compatibility, staged rollout with a percentage rather than everyone at once, and the ability to roll back by republishing the previous bundle. Ask a candidate how they have handled the rollback case. If updates are new to them, they will describe the happy path only, and the happy path is not where this hurts.

Upgrades Are the Defining Maintenance Cost

If you take one budgeting lesson from this page, take this one. The recurring cost of a React Native app is not features and it is not bugs. It is staying current, and teams consistently fail to plan for it because on the web the equivalent work is a dependency bump on a Friday afternoon.

Why it is harder here

A React Native upgrade is really four upgrades wearing one version number. The JavaScript packages move. The iOS project moves, including pods, deployment target and sometimes Xcode itself. The Android project moves, including Gradle, the plugin and the Kotlin version. And every native dependency you have must be compatible with all of that simultaneously. Miss one and the failure appears as a build error in a file you have never opened, written by somebody who is no longer maintaining the library.

The transitive native dependency trap

Here is the pattern that stalls real projects. You depend on a chart library. It depends on a native drawing library. That library was last touched three years ago and does not compile against the current Android Gradle Plugin. Now your framework upgrade is blocked by a package nobody on your team chose, and the options are: fork and fix it, replace the chart library and rewrite every chart, or stay where you are. Teams pick the third option, silently, and that is how an app ends up five majors behind. Every additional major makes the eventual move worse, because you now need the intermediate states as well.

Tooling that helps

The community Upgrade Helper shows the exact diff of the template between any two versions, which turns the native project changes from archaeology into a checklist. Expo pins a compatible dependency set per SDK release, which is a large part of why managed projects upgrade more calmly, and its doctor command will tell you what disagrees before you find out from a build. Neither removes the work. Both remove the guessing.

The cadence to insist on

Upgrade on a schedule rather than on demand. Every two or three releases keeps each step small, keeps you inside the window where community libraries have caught up, and keeps the work inside a sprint instead of inside a project plan. It also means that when Apple or Google sets a deadline that forces a rebuild, and they do this regularly, you are one small step from compliant rather than eighteen months from it. Android's shift to sixteen kilobyte memory pages is a recent example: every bundled native library has to be rebuilt for it, which is trivial if your dependencies are current and a serious problem if they are not.

What to ask about it

Ask a candidate to describe the last upgrade they did, by version. The good answer names the versions, names the dependency that caused trouble, and explains what they did about it. The weak answer is that upgrades generally go fine. Nobody who has done several of these thinks upgrades generally go fine.

Debugging Across the JavaScript and Native Boundary

A bug in a React Native app can come from three places, and the whole skill is deciding which one you are in before spending a day in the wrong tool.

The three origins

Your JavaScript

The comfortable case. React Native DevTools, breakpoints, component inspection, network view. Flipper stopped being the default tool some releases ago and the current debugger is where the tooling effort went, so a candidate still describing a Flipper workflow is telling you when they last set up a project.

A native module

Now you are in Xcode or Android Studio, attaching to the process, reading Logcat, stepping through Kotlin. This is the boundary most React developers will not cross, and it is where the hard bugs live.

The platform itself

The OS killed your process, a manufacturer's power manager stopped your background task, an OS update changed a permission default. No amount of reading your own code finds these. You find them by knowing they exist.

Why release builds are a different animal

The build that reaches users is not the build the developer ran. Minification has renamed everything. Hermes has turned your code into bytecode. Development-only checks are gone. Metro is not serving anything. So a crash report from the field arrives as a stack of meaningless symbols unless the source maps, the iOS symbol files and the Android mapping file from that exact build were uploaded to your crash reporter at build time. Getting that pipeline right is a half day of work that pays for itself the first time a production crash appears, and its absence is the reason so many teams stare at unreadable reports and guess.

The physical Android device problem

Simulators lie. They lie about performance, because they run on your desktop processor. They lie about memory pressure. They cannot reproduce a manufacturer's aggressive background killing, thermal throttling, a real camera, real Bluetooth, or a genuinely poor network. A large share of the bugs that reach customers are bugs that only exist on real hardware, usually cheap Android hardware two or three years old.

This is why the interview question about a crash that only reproduced on a physical Android device works so well. It is a universal experience for anyone who has shipped, and the answer contains the whole method: how they got the device logs, whether they knew to look at the mapping file, whether they suspected the manufacturer layer, how they built a reproduction. Nobody can improvise that convincingly.

How Do You Screen a React Native Developer in an Hour?

Skip the algorithm exercise. It tells you about the JavaScript half you were never worried about. Four questions do almost all of the work, and every one of them asks for something that happened rather than something known.

1. Tell me about a crash that only reproduced on a physical Android device

The best opening question in this stack. What you want to hear is a specific device or manufacturer, how they got hold of the logs, and a cause from the real list: minification stripping something reflection depended on, a background restriction, a permission behaving differently on that OS version, a native library missing an architecture. What you do not want is a general statement that Android fragmentation is challenging. Everyone knows that. Only people who have done the work can tell you what they typed.

2. Tell me about a list that stuttered, and what you actually changed

Ask how they knew it was slow, not just that it was. A good answer mentions measuring rather than guessing: a profiler, a frame trace, counting renders. Then the fix, and the fix should be specific. Memoising the row component. Replacing an index key. Providing item layout. Moving image decoding. Switching list implementations and dealing with recycling. A weak answer is that they added memo everywhere and it seemed better afterwards.

3. Tell me about a native module you wrote or patched

This is the question that separates the two kinds of candidate, and there is no faking it. Expect the library name, the platform, the language, what was wrong, and what happened to the fix afterwards. Someone who patched a community package and carried the patch through two upgrades has told you a great deal in one story. Someone who has genuinely never needed to is not disqualified, but you now know the shape of the team you must build around them.

4. Walk me through the last React Native version upgrade you did

Versions, blockers, decisions. If they name the dependency that fought them, they were there. If they describe the upgrade helper diff and what they did with the native project changes, they were definitely there. If upgrades come up as an untroubled routine, they have either been very lucky or they were not the one doing it.

What weak answers sound like

Everything is a library recommendation

Asked about a problem, they name a package. Package knowledge is real but it is not diagnosis, and when the package is the problem this candidate has nowhere to go.

No numbers anywhere

Performance work described entirely in adjectives. Faster, smoother, better. Someone who profiled will reach for frame rates, startup times or render counts unprompted, because that is what convinced them it worked.

Release is somebody else's job

No opinion on staged rollout, no story about a rejection, no crash dashboard habit. In mobile, shipping is part of engineering, and a person who has been kept away from it will need supervision at exactly the wrong moment.

The practical exercise, if you want one

Give them a small existing app with a deliberately awful list and ask them to make it smooth, out loud, in forty minutes. You learn more from watching where they look first than from any take-home. The strong ones measure before they touch anything. The rest start adding memo.

React Native, Flutter or Native: The Honest Version

We build in all of these and have no incentive to sell you the wrong one, so here is the comparison as we actually make it internally.

Where React Native is the right call

You already have React people. That is the strongest single argument and it is a hiring argument rather than a technical one: your existing engineers become partly useful on mobile immediately, your types and validation and API layer are shared, and recruiting stays in the JavaScript pool, which is the deepest pool in India by a wide margin. It is also the right call when the app is fundamentally screens over an API with a normal amount of interaction, which describes most business software.

Where Flutter wins

When the interface is unusual and identical everywhere by design. Flutter draws its own widgets rather than mounting platform ones, so a heavily custom design system behaves the same on both platforms without a per-platform argument every sprint. It is also a calmer story for animation-dense work. The cost is a Dart team you now have to hire and keep, separate from your web engineers. If that trade suits your situation, our Flutter developers in India page goes through it properly.

Where you should just build native

When the platform is the product. Continuous camera or sensor pipelines, serious background processing, widgets and watch and system integrations, apps that must adopt new OS features the week they ship, and anything where a dropped frame is a defect rather than an annoyance. In those cases a cross platform layer is something you fight rather than something you use, and you are better served by iOS developers and Android developers who live in one platform each.

The code sharing question, answered without marketing

You will not share your screens with your web app in any meaningful way. There is no DOM and no CSS, so the rendering layer diverges by construction, and every attempt to build a component abstraction across both ends up as a layer that is harder to maintain than the two implementations it replaced. What does share, cleanly and permanently: types, validation schemas, the API client, business rules, formatting, and a good deal of state management. That is a real benefit. It is just not the one the diagrams imply, and any candidate who promises you shared UI has not tried it.

The trap in the middle

The worst outcome is choosing React Native for the code sharing story, staffing it entirely with web developers, and discovering in month four that nobody can go below the seam. You get an app that is ninety percent finished and permanently blocked on the last ten percent. If you take one structural decision from this page, take that one: cross platform saves you a platform, it does not save you platform knowledge.

Three Situations You Might Be In

These are recurring patterns rather than particular clients, and they need different first moves.

The app you inherited

An agency built it, the relationship ended, and you have a repository, a store listing and nobody who understands either. It is several majors behind, the build works on one machine, and there is a patches folder nobody can explain.

The wrong first move is a feature. The right one is two weeks of establishing facts: get it building from a clean checkout on a machine that has never seen it, list every native dependency and its maintenance state, work out which version wall you are behind, wire up crash reporting with proper symbols, and confirm the store accounts and signing keys are in your company's name. Then decide. Half the takeovers we look at need an upgrade before anything else, and the other half are fine and just need someone who is not afraid of them.

The web team told to ship an app

You have a good React team, a mandate for mobile, and a deadline. React Native is the obvious answer and it is probably correct. The failure mode is predictable: the first six weeks go beautifully and then the app hits push notifications, background behaviour, a store rejection or a device-only crash, and progress goes flat.

The fix is cheap if you do it early. Add one engineer who has shipped mobile before, alongside your own people rather than instead of them. Their job is the seam, the release pipeline and the questions your team does not yet know to ask. Six months later your team has absorbed most of it and you can decide whether the slot stays.

The app that works but feels cheap

It ships, it functions, and users describe it as slow or janky without being able to say where. Reviews mention lag. Retention on Android is worse than iOS and nobody knows why.

This is measurement work before it is engineering work. Frame timings on real mid-range hardware, cold start broken down into what happens before the first paint, render counts on the main list, and crash and hang rates read per app version rather than in aggregate, because a healthy old version hides a bad new one. The usual findings are unmemoised rows, an eagerly initialising module set, images decoded at full resolution, and animation driven from the JavaScript thread. All fixable, none of it guessable.

What Working With an India-Based React Native Team Looks Like

Distance is arithmetic, so let us do the arithmetic instead of offering comfort. India Standard Time is pinned at UTC+5:30 all year and there is no daylight saving in this country at all. When the distance between your desk and this one changes, it changed because you moved your clocks. Nothing happened here.

The overlap, worked out

Our people start at 09:30 IST, which is 04:00 UTC, and finish at 18:30 IST, which is 13:00 UTC. Hold that nine hour block against a nine to five wherever you happen to sit. London in summer sees 05:00 to 14:00 local, so you share five hours from the moment you arrive; in winter it is four. Amsterdam and Berlin get six in summer. Dubai shares effectively the whole day. Singapore picks us up from midday. Sydney sees 14:00 to 23:00 local during standard time, which gives three hours at the end of your afternoon, and Auckland gets one. New York in summer sees 00:00 to 09:00, which finishes at the exact minute you sit down, and in winter it stops an hour before that. San Francisco is further out still.

Read that plainly. Europe, the UK, the Gulf and Singapore get a shared window at no cost to anyone. The Australian and New Zealand overlap is thin and sits right at the close of your afternoon. North America gets nothing whatsoever from a default Indian schedule, and a vendor claiming otherwise has redefined business hours without telling you.

What an American window costs

It is entirely doable, and it is a rota you staff rather than a kindness you request. Sharing four hours with New York means the Indian day sitting around 13:30 to 22:30 IST, which produces a common 09:00 to 13:00 Eastern. West coast needs it pushed later again. Those are evening shifts for the people working them, and that shapes who takes the role and how they feel about it in month five. We would rather name the window, write it into the engagement and hold it steady than let it drift. Continuous cover across all hours is a different thing again: it needs several sets of people, a written handover every day, and a coordination overhead most products this size should not pay. If you want it, we will scope it as the thing it is.

The update rhythm, which is specific to this stack

Here is a React Native detail that generic offshore advice misses entirely. Because most fixes can go out as a JavaScript bundle rather than a store build, the distance between us matters much less than it does on a native project. A bug you report at four in the afternoon in London is diagnosed and pushed the same Indian evening, and your users have it before you are back at your desk. That is a real advantage of this stack for a distributed engagement, and it is worth designing around.

It has a matching hazard. A bundle pushed at the end of the Indian day produces its first crash data while everyone here is asleep. So agree three things once, up front, and never think about it again: what percentage a first rollout goes to, what error rate triggers an automatic halt, and who is authorised to roll back without waiting for a call. One conversation removes a whole category of bad nights.

The device list

The handset list comes out of your analytics, not out of a launch calendar. Whichever manufacturers show up heavily in your install base go on it, since each of the large Android vendors layers its own power management over the OS and each one terminates background work by different rules. There is always at least one budget phone a few years old in the set, because that hardware surfaces slow frames and memory pressure long before a current flagship ever will, and it is the hardware most of your users are holding.

Working practices that survive the distance

Small pull requests into your repository

Your repo, your review rules, branches short enough that someone eight hours away can read one without booking a call. A large branch landing on a Friday is the fastest way to make remote review stop functioning.

Written before spoken

Anything a call settles goes into the ticket in writing before anyone logs off. Not as ceremony. Whoever slept through that call has no other way to act on the outcome, and twelve months later it will be the only surviving explanation for a decision somebody is questioning.

Accounts and keys stay yours

Apple Developer Program membership, Play console, repository and update service all in your organisation's name, with engineers added as users holding defined roles. Nothing has to be handed back at the end because nothing was ever held.

Done means installed

A merged pull request does not close a ticket. It closes when a build is on TestFlight and an internal Play track, installed on the target handsets, with its frame timings and crash rate compared against the previous version.

English is assessed, not assumed

The thing that matters at this distance is written clarity: can this person describe a problem in three sentences that make sense to someone with no context. We screen on that specifically, because it predicts the engagement better than an accent ever will.

You talk to the engineer

Not to an account manager relaying technical detail. The person writing the code attends your standup and answers your questions directly, which also means you can tell within two weeks whether the match is right.

What If the Engineer Is Not the Right Fit?

Every offshore buyer has this question and most vendor sites answer it with a paragraph about culture. Here is the mechanical answer instead.

Replacement, and choosing rather than being assigned

If the person is wrong for the work, a replacement arrives inside 48 hours, and the choice belongs to you: several candidates to see and pick between, not one substitute handed over with a hope attached. Raise it early. The expensive version of this problem is the client who says nothing until the quarter ends because mentioning it felt rude.

Why the switch is cheap when the practices are right

A handover hurts in proportion to how much only lived in one person's head. If work has been arriving as small reviewed pull requests in your repository, if decisions were written down the day they were made, and if the build runs from a clean checkout on a machine that has never seen the project, then a new engineer is productive in days. If none of those hold, any change of person is painful regardless of who supplies them. That is the actual argument for those practices, and it is why we do not treat them as optional.

Why matching is quick in the first place

Nobody gets recruited against your brief. The engineers are already here, not working out a resignation somewhere else while you hold a project open. That is the whole reason matched profiles can be in front of you two days after we speak, on a 48 hour clock, and why a start date seven days out is a date rather than a hope.

Ownership, access and exit

Commits land in your repository from day one instead of arriving as a delivery at the end. Store and update accounts sit in your company's name with engineers added as users. Access is granted rather than transferred, so ending an engagement is a matter of removing users. The specific contractual terms, including how notice works in either direction and what the confidentiality and data handling clauses say, live in the agreement we sign before anyone writes code, not in marketing copy on a web page.

Ways to Work With Us

An engineer inside your team

You run the backlog, the standup and the review. They are your engineer, working your hours in the window you agreed, in your tools. This suits teams who already know what they are building and are short of hands rather than short of direction. It is also the model that gives you the cleanest read on the person, because you see their work daily.

A pod that owns the app

Two or three engineers with the native slot explicitly filled, plus test coverage, taking a product from brief to store. Right when you have no mobile capability in house and no desire to build one this year. The thing to insist on is that the pod works in your repository and your accounts from day one, so there is never a delivery event where ownership changes hands.

Assessment or rescue

A fixed piece of work with a written output rather than an open-ended relationship. An inherited app assessed and its upgrade path costed. A performance investigation that ends in measured findings. A version upgrade planned and executed. Useful when you need to know what you actually have before committing to anything larger.

Frequently Asked Questions

Is React Native fast enough, or should we build native?

For most product apps, yes. Screens over an API, forms, feeds, chat and checkout all run fine. The places it strains are long lists with heavy cells, continuous camera or sensor work, and animation that has to stay glued to a finger under load. Those are solvable with native views and worklets, but solving them needs someone who can write the native side rather than only configure it.

How do I tell a JavaScript developer from a mobile engineer in an interview?

Ask about the last bug that only appeared on a real Android handset in a release build. A JavaScript developer describes reading logs and guessing. A mobile engineer describes reaching for Logcat, a symbolicated stack trace, and the mapping file from that exact build, then names the culprit: minification, a Doze restriction, or an OEM battery setting.

Should we start on Expo or bare React Native?

Start on Expo unless you already know you need native code the SDK does not cover. Prebuild and config plugins mean you can add custom native dependencies later without abandoning the tooling. Choosing bare on day one to keep options open usually just buys you two build systems to maintain before you have a product worth maintaining them for.

Can we ship fixes without waiting for App Store review?

JavaScript changes, yes. Over the air updates replace the bundle your installed binary loads at launch, so copy fixes, logic fixes and most UI changes go out in minutes. Anything touching native code, permissions, entitlements or the SDK set needs a new binary and a review queue. Store rules on this shift, so read the current wording before you build a release process around it.

How bad are React Native version upgrades really?

The framework itself is manageable. The pain comes from your native dependencies. One unmaintained library that never moved to the New Architecture can stall an entire upgrade, and you find out only after the build fails. Teams that upgrade every few months barely notice. Teams that skip two years discover the upgrade is a project with its own budget.

How much code can we actually share with our React web app?

Validation schemas, API clients, types, formatting helpers and a good deal of state logic share cleanly in a monorepo. Components mostly do not, because React Native has no DOM and no CSS. Teams that expect to share screens end up writing a compatibility layer that costs more than the duplication it was meant to remove.

Our engineering team sits in New York. Does an India timezone actually work?

Not on a default Indian schedule. IST sits at UTC+5:30 permanently, so a 09:30 to 18:30 day in Mumbai runs from midnight to 09:00 Eastern in summer and stops at the moment you arrive. Four shared hours means staffing the Indian side closer to 13:30 until 22:30 IST. Perfectly workable, and it is a rota someone has to agree to and keep working.

Suppose the engineer we choose turns out to be a poor fit?

A different engineer reaches you inside 48 hours, and you pick that person from several candidates rather than being assigned one. Flag it early instead of waiting for a sprint boundary. The swap itself costs very little as long as the work has been arriving in your repository as small reviewed pull requests, which is the practical reason we insist on working that way.

Tell us what state your React Native app is in

A first build, a takeover from an agency that stopped answering, a version wall nobody has costed, or a list that stutters on every Android phone in your support inbox. Describe the situation and we will tell you what level of engineer it needs, and whether React Native is even the right answer for it.