When I built my first React Native app, I had some prior experience in web development. Using React for iOS and Android seemed like a natural extension of my skills.
But I quickly discovered — the hard way — that a web-developer mindset doesn’t always translate well to native app development.
Take navigation, for example. On the web, each site typically crafts its own unique page layout. Headers, sidebars, and footers are all custom-built. You might start with a simple element, but it's just a blank canvas. You have to use JavaScript and CSS to make it functional and visually appealing.
This customization becomes part of your brand identity. If your header looks like another site’s, it feels generic.
When I transitioned to native app development, I brought that same mindset with me.
I meticulously designed custom components for everything. I built my own header with a back button and title. I created custom screen transitions and animations. I even managed the safe area myself and tracked screen orientation to animate transitions. I crafted custom bottom tabs — anything to ensure my app didn’t look like everyone else’s.
It didn’t take long to realize this was a mistake.
Native app users expect consistent patterns across apps. They also have high expectations for the quality of basic UI elements, like navigation. For instance, on iOS, users expect to long-press the back button to navigate several screens back. If that’s missing, the experience feels incomplete.
So why reinvent the wheel? Native components are already optimized for the platform. Instead of building a custom header from scratch, I could have simply used the native one and tweaked its colors. Better still, I could’ve skipped customization altogether and used UIKit components directly. Far from feeling "basic," users would have appreciated how naturally the app fit within the iOS ecosystem.
Take the success of apps like Apollo for Reddit. People loved it because it embraced iOS’s native design language, making it feel like a natural extension of the platform.
This contrasts sharply with web development. On the web, you start with a blank canvas and build everything yourself. Out-of-the-box primitives are often clunky and unappealing. Buttons are gray. Inputs have harsh outlines. You even need a CSS reset just to get a consistent baseline.
Web UI libraries exist to alleviate these frustrations, but they’re a patchwork solution. By comparison, native platforms like iOS come with cohesive, beautifully crafted design primitives. Navigation, animations, menus, typography, colors, and icons are all carefully designed by experts to create seamless user experiences. These aren’t just tools — they’re a design system shaped by decades of refinement.
It took me far too long to appreciate this. I didn’t even bother to read Apple’s Human Interface Guidelines initially.
Back in 2019, React Native apps often felt like high-quality imitations of native ones. They looked the part but didn’t quite feel it. React Native's philosophy has always been to match the underlying platform, but in practice, many libraries relied on JavaScript-based components that mimicked native ones instead of using the real thing.
For example, React Native apps on Android often used Material Design headers, but they were implemented in JavaScript rather than leveraging the actual native header components.
As a web developer, I found this abstraction appealing. It gave me complete control over customization. But this approach often led to subtle inconsistencies that diminished the user experience.
React Native developers weren’t entirely to blame. At the time, working with native code was cumbersome, especially if you were using Expo. Adding native features often required abandoning Expo and reconfiguring your entire stack. Libraries with native code frequently fell out of sync with React Native updates, leaving developers to grapple with Objective-C or Java files they didn’t fully understand.
“JS-based” even became a selling point for libraries, promising developers they wouldn’t have to deal with native dependencies.
This divide between JavaScript and native code reinforced my web-first mindset. Why not stick with JavaScript for everything?
Thankfully, the React Native ecosystem has evolved. Today, you can write native modules using Swift and Kotlin with far less configuration. Even better, Expo now supports native code. These improvements have made native-first development more accessible, encouraging libraries to embrace native primitives.
React Navigation, for instance, has shifted towards using native components, prioritizing a seamless user experience over extensive developer customization.
This shift signals a brighter future for React Native. With native tools becoming easier to use, we can build apps that look and feel like they belong on their platforms.
So, learn from my mistakes. Use the platform's tools and embrace its design philosophy. And above all, master the rules before you decide to break them.
Top comments (0)