Why teams choose React Native
The strongest argument for React Native is usually organizational rather than technical. If your company already builds its web product in React and TypeScript, a React Native app can share people, patterns, libraries and sometimes code with it. Engineers can move between web and mobile, code review standards stay consistent, and business logic such as validation, API clients and formatting can live in shared packages.
Unlike Flutter, React Native renders real platform UI components, so text inputs, scroll views and accessibility behave the way each OS expects by default. If you want a fully custom, pixel-identical UI and have no React investment to reuse, building with Flutter may suit you better. We'll say so if that's the case.
- Shared skills: React, hooks, TypeScript and testing tools your engineers already know.
- Shared code: API clients, schemas, state logic and utilities in a monorepo used by web and mobile.
- Native feel: platform components and gestures, with native code where the experience demands it.
- Faster fixes: JavaScript-only changes can ship as over-the-air updates within store rules.
TypeScript and the New Architecture
Every project we start uses TypeScript in strict mode. Types catch mismatches between the app and your API before QA does, and they make large refactors far safer for a distributed team.
React Native's New Architecture is now the default for new projects. It replaces the old asynchronous bridge with JSI, a direct interface between JavaScript and native code, and brings three main pieces:
Fabric
The new rendering system. It supports concurrent React features and synchronous layout reads, which removes some long-standing UI glitches.
TurboModules
Native modules that load lazily and are called through JSI, reducing startup cost and call overhead.
Codegen
Generates typed native interfaces from TypeScript specs, so the contract between JS and native code is checked at build time.
Expo or bare workflow
Expo has matured into the recommended way to start most React Native apps. The old objection that Expo blocks custom native code no longer holds: development builds and config plugins let you add native modules while keeping Expo's tooling. Bare projects still make sense in some situations.
| Aspect | Expo (with prebuild) | Bare React Native |
|---|---|---|
| Native projects | Generated from config, so ios and android folders can be recreated rather than hand-maintained. | Committed and edited directly by the team. |
| Custom native code | Supported through Expo Modules and config plugins. | Full, direct control. |
| Upgrades | Usually simpler, since SDK versions bundle compatible library versions. | More manual work across native project files. |
| Best for | Most new apps and teams without deep native expertise. | Apps embedding React Native into an existing native app, or heavy custom build setups. |
EAS builds, OTA updates and store policy limits
Expo Application Services (EAS) handles cloud builds, signing credentials and store submission, which removes the need for every developer to maintain a working Xcode and Android signing setup. You can also keep builds in your own CI if you prefer. Either way, credentials live in your accounts.
Over-the-air updates, used responsibly
Because the app's JavaScript bundle is loaded at runtime, tools such as EAS Update can push fixes to installed apps without a store release. That is powerful and easy to misuse. Both Apple's and Google's rules allow updating interpreted code, but not using it to change the app's primary purpose or add features that bypass review. We follow a simple policy:
- OTA for: bug fixes, copy changes, small UI adjustments and configuration within features that were already reviewed.
- Store release for: new features, new permissions, any native code or dependency change, and anything affecting payments.
- Runtime versioning so an update built for one native version is never delivered to an incompatible binary.
- Staged rollout and rollback for updates, the same way we treat store releases.
Writing native modules
Most apps eventually hit something the JavaScript ecosystem doesn't cover well: a vendor SDK with only native libraries, a background task, a home screen widget or a performance-critical camera pipeline. Our team includes engineers comfortable in Swift and Kotlin who write these modules properly rather than patching a half-maintained package.
- 1
Define the TypeScript interface
Agree the API from the JavaScript side first, so screen work can continue against a mock.
- 2
Implement per platform
Write the Swift and Kotlin implementations as Expo Modules or TurboModules, keeping platform differences behind one interface.
- 3
Test natively and from JS
Native unit tests for the platform logic, plus end-to-end tests that exercise the module through the app.
- 4
Document and own it
Native modules live in your repository with notes on upgrade risks, so future SDK updates aren't a mystery.
Performance profiling
React Native performance problems usually come from JavaScript doing too much work on the wrong thread, not from the framework itself. We measure before optimizing, on real mid-range Android hardware where problems show first.
| Symptom | Typical cause | What we do |
|---|---|---|
| Janky scrolling in long lists | Heavy item components and unnecessary re-renders. | Virtualized list libraries, memoized rows and stable keys, verified with the React profiler. |
| Stuttering animations | Animation logic running on the JS thread. | Move animations and gestures to the UI thread with libraries built for that. |
| Slow startup | Large bundle, eager imports and too much work on launch. | Hermes bytecode, lazy loading of screens and deferring non-critical setup. |
| Memory growth | Leaked listeners, uncached or oversized images. | Heap snapshots plus native profilers in Xcode Instruments and Android Studio. |
Working alongside your web team
Because React Native shares so much with React on the web, the way our team plugs into yours matters. We usually set up a monorepo with shared packages for types, API clients and design tokens, and agree ownership boundaries so mobile and web teams don't block each other's releases.
- Shared lint, formatting and TypeScript configuration across web and mobile.
- One API schema generating types for both clients.
- Joint code review on shared packages, separate review on platform-specific code.
- Mobile-specific QA, since a passing web test suite says little about gestures, permissions or offline behavior.
For longer roadmaps, many clients prefer a stable, dedicated mobile squad that stays with the product, and after launch we keep dependencies and React Native versions current through ongoing app maintenance.