Decisions that shape a React Native app
What gets shared with the web, and what does not
React Native renders real native views, not HTML, so your web components do not carry over. What does carry over is everything underneath: TypeScript types, API clients, data-fetching hooks, form validation, state stores, date and currency logic. We put those in shared packages inside one repository, with the web app and the mobile app as separate front ends. That is where the real saving sits. A pricing rule changes once, and both products get it in their next release. The screens themselves are written for touch and for the navigation habits of each platform, which is as it should be.
Expo or a bare project
Expo used to mean giving up native code. It no longer does. With development builds and config plugins, an Expo project can include custom native modules while Expo handles the build setup, upgrades and over-the-air updates. We start most new apps on Expo for that reason. A bare project, where you own the iOS and Android folders directly, still makes sense when React Native is being added to an existing native app, when the build needs unusual native configuration, or when your team already has native engineers who want that control. Moving between the two is possible, so this is a decision about convenience today and not a permanent lock.
Native modules and navigation
Every serious app eventually needs something with no JavaScript package. We write those modules in Swift and Kotlin, type their interfaces and test them like any other code. Navigation gets the same early attention. We use React Navigation with native stack screens, type the route parameters and define the deep link map at the start, because retrofitting links from push notifications and emails into an unplanned navigation tree is slow, error-prone work.
Performance, honestly
Your application logic runs in JavaScript on its own thread, while the interface is native. Most apps never notice the boundary. Problems appear when that thread is busy during a gesture or a scroll: large lists that render every row, components that re-render on each keystroke, animations driven from JavaScript. The fixes are well known. Virtualize lists, memoize what is expensive, run animations and gestures on the UI thread with Reanimated, and measure on a mid-range Android phone.
Where React Native wins and where it does not
It beats Flutter when you have React developers, a TypeScript codebase to share and a hiring pool that already knows the tools. It does not when the team would be learning React from zero, when the design depends on heavy custom graphics and animation, or when you want as little dependence on third-party libraries as possible. In those cases Flutter or native is the more comfortable choice, and we build both.