← back to writing

Shaping a Better Cross-Platform Experience

The journey from building missing components to creating NativeUI Primitives - a foundation for true cross-platform native experiences.

November 4, 2024

A few months back I landed on a new project: build a web app and a mobile app for a client, with exactly the same features on all platforms. Nothing dramatic in theory. The API was already built, nicely documented, and working. The challenge was choosing an approach that didn't force me into maintaining two different frontends at once.

So I started with the classic thought every developer has at least once: make a PWA, wrap it as a mobile app, and ship it. Problem solved, right? Except I needed a set of device APIs that the web simply doesn't expose. That limitation pushed me toward React Native, where I could target Android, iOS and the web with the same logic but still reach the device capabilities I needed.

Exploring the Ecosystem

Before building anything myself, I explored the ecosystem. Gluestack was the first library I stumbled on. I liked the developer experience and built a quick MVP with it, but a few important components were missing for my project: combobox, date picker, calendar and a couple of others. Then I tried React Native Reusables. It felt like the React Native equivalent of shadcn/ui, but again, some of the exact components I needed weren't there.

So I did what any stubborn developer eventually does: I built the missing pieces. That became the first version of NativeUI.

The First Version

I released it open-source, which was a first for me. After two months of development it reached a stable point, I integrated it into my MVP, and I was genuinely happy with how it turned out. People gave feedback, some helpful, some… less helpful. But a pattern emerged. Users expect mobile apps to feel native. Not fake-native, not "close enough," but genuinely aligned with the platform's behavior. And that was the weak spot of NativeUI's first version.

The Realization

The realization hit me while scrolling through X and seeing HeroUI announce HeroUI Native. Their demos looked deeply native, the kind of texture and behavior users instantly recognize. My components looked good, but they didn't carry that underlying, platform-level identity. That's when it clicked: building components was the easy part. Building proper primitives was the real foundation.

shadcn/ui is powerful because of how both layers work together. The beautifully crafted components are what developers reach for, nobody wants to spend time styling primitives from scratch when trying to ship fast. But those components are only as good as their foundation. Solid primitives make everything consistent, accessible, and predictable. You can't have great components without great primitives, and primitives alone aren't the end goal. Both matter. NativeUI didn't have that foundation yet.

The Pivot

So I pivoted. If I wanted to offer a real cross-platform experience with true native feeling, I needed a proper base. A shared API. A unified approach for Android, iOS and web. Something unstyled, focused on behavior, not aesthetics.

That's how NativeUI Primitives started.

The idea is simple. First build a solid library of primitives that behave natively across platforms. Then rebuild NativeUI on top of those primitives with styling and animations. And keep the shadcn philosophy alive: copy, paste, ship.

Where We Are Now

NativeUI Primitives is now live in beta. The core primitives are already available. If you want to see them in motion, I shared a few previews on X:

And if you want to try the components yourself, explore the docs, or check the roadmap, everything is here:

This project started because a few components were missing. It grew into building the foundation I wish existed when I started. There's a lot more to explore from here, especially now that the base feels right.

Shaping a Better Cross-Platform Experience | Theo Ribbi