I build mobile products in TypeScript, and the constraint is always the same: one team, two stores, a web surface that should not be a third rewrite, and a release process that does not stall on a laptop's Xcode install.
Expo is still the default I reach for, because OTA updates, cloud builds, and a shared React Native codebase are the parts of mobile I do not want to own by hand.
What I have actually shipped
LegalAgent is the current Expo production case: voice and chat in React Native, RAG over case material, and an admin surface for prompts and documents. Expo is how that client ships to phones without a native team in the loop for every JS change.
Before that I spent real time in React Native without treating Expo as the whole story. Make Sense Labs was Sense.chat, with messaging, an EOS wallet, and privacy features, and Tractor Supply was a retail app with browsing, accounts, and AR product previews on ViroAR. Those codebases taught me the native edge cases that Expo does not erase, such as AR, store review, and performance, and they also taught me why I do not want to rebuild certificates and build graphs for the next product when EAS will hold them.
What Expo is for
React Native is the bridge from React to iOS and Android, and Expo is the toolchain around that bridge: a module set, a build service, and a publish path that does not start with opening Xcode. The parts I rely on:
Over-the-air updates. JS and assets go out without waiting on App Store or Play review. Store review still applies to native binary changes, but for copy, flags, and a large class of bugfixes, OTA turns a week in review into a same-day patch, which matters on products where the JS layer is the product.
Cloud builds. EAS compiles on Expo's machines, so I do not maintain a blessed macOS image to keep Android and iOS building, and certificates and profiles live in that service instead of on a wiki page.
Native APIs through modules. For camera, notifications, biometrics, and media I reach for the Expo module first. Custom native code is the exception, and Expo's bare and continuous native paths are there when the exception is real.
React Native Web and NativeWind. Shared TypeScript types, shared business logic, and one styling approach mean a single authentication flow and the same domain model on a phone and in a browser, without a second design system.
React Native in the wild
I do not need a logo wall to justify the engine. Coinbase published their move to React Native and called out OTA as part of how they ship, Meta built the framework, and Discord, Shopify, and wallets like MetaMask Mobile, Rainbow, and Ledger Live run on it. That is enough evidence that RN is a production stack, and Expo is how I operationalize it on a small team.
When I do not use it
Some products need more than Expo's modules cover:
- Heavy 3D, custom AR/VR, or a graphics pipeline outside the module set, which Tractor Supply's ViroAR work reminds me of.
- Narrow native modules with no Expo equivalent.
- A thin shell over a large existing native app.
- Millisecond-sensitive loops, such as HFT-style clients or competitive realtime games.
For those I look at a development build or a fully native app. The cases are real, and they are also a small share of the product work I am hired for, which is mostly chat, accounts, media, AI surfaces, and commerce.
Where Expo stays my default
If the team already writes TypeScript and React, Expo is the cheapest honest path onto phones: one codebase, EAS for binaries, OTA for the JS layer, and native modules until they stop being enough. LegalAgent is why I still pick it, and Sense.chat and Tractor Supply are why I know where it stops.