Going Universal: From a brownfield React Native and Next.js stack to one Expo app
Article Summary
Gunnar Torfi shares how a single developer maintained 40+ mobile screens AND a full web app by merging them into one universal codebase. No big rewrite required.
The Noona team was drowning in duplicate work: separate React Native and Next.js codebases meant building every feature twice. With just one developer maintaining the product, they gradually migrated to a single Expo app that runs on iOS, Android, and web without sacrificing velocity.
Key Takeaways
- Started with one shared Button in Turborepo to prove the pipeline worked
- Built checkout (their most complex screen) as first shared feature
- Used temporary provider pattern to avoid months-long auth/analytics rewrite
- Migrated incrementally: only refactored features when actively shipping them
- Achieved simultaneous web and mobile releases with zero coordination overhead
A small team unified their brownfield React Native and Next.js stack into one Expo app over several months while continuously shipping product, eliminating duplicate work and achieving feature parity across platforms.
About This Article
Noona maintained separate codebases for Expo and Next.js, which meant every bug fix and feature had to be built twice. This created duplicate work and made code reviews exhausting.
The team set up a Turborepo monorepo with shared UI packages using React Native primitives and react-native-web. They used platform-specific file extensions like .native.ts and .web.ts to handle components such as Image, Link, and payment handlers.
Features are now organized in packages/ui/features/*, so Noona ships to web and mobile at the same time without needing to coordinate releases. New features get written once and work on both platforms immediately.