LFH Lab Start a project
Lab Notes/Building
BUILDINGJUN 2026

One codebase, two app stores

I've shipped dozens of apps to both stores, in native code, React Native, and a few compilers I'd rather forget. Here's why the ones I build now are Expo, and when they aren't.

I've shipped dozens of apps to both app stores. Some were native, written twice. Some were React Native. A few went through cross-compilers that promised one codebase and delivered one codebase plus a second one to work around the first.

The three mobile products I run today are all Expo. That's not because it's the only thing I know. It's because after enough of the alternatives, it's the one I'd pick again for the kind of app I usually build. This is where that line sits.

The three apps

FreelyGiven is an AI Bible study app in ten languages. Sneaker Kings is a sneaker trading game. Defenders Gateway is a client build serving veterans, first responders, and Gold Star families, with thousands of users. Three products, three audiences, one stack: Expo on the front, Node and MongoDB behind it, RevenueCat on top of both stores' billing.

What native-twice actually costs

Writing an app twice isn't twice the work. It's more, because the two versions drift. A feature ships on iOS in March and Android in April. A bug gets fixed in Swift and forgotten in Kotlin. Two review queues, two sets of platform rules changing on two schedules, two places every design decision has to be made and then made to match.

With a team, that's a coordination cost. Alone, it's a choice between platforms, and the honest version of "we'll do Android later" is "we won't." I've been on teams where the Android app was eighteen months behind and everyone had stopped mentioning it.

What the cross-compilers cost

I've used the tools that compile one language into both platforms' native code. They work, right up to the edge of what the tool's authors anticipated. Then you're reading a bridge layer's source at midnight to find out why a list scrolls wrong on one platform, and the fix is a patch to the tool, not to your app.

The pattern was consistent: fast start, then a long tail of "the framework doesn't quite do that," and a dependency on a vendor whose roadmap you don't control. Some of those vendors are gone now. Their apps still needed maintaining.

Why Expo, for these apps

One feature, both stores, same day. Every screen I build lands on iPhone and Android together, because there's only one screen. For a one-person shop this isn't convenience; it's the difference between having an Android app and not having one.

Updates that skip review. Most of what changes in an app is JavaScript: copy, logic, screens, fixes. Over-the-air updates push those without a store submission. A bug reported at 9am is fixed on users' phones by lunch. On a client app with thousands of users, that has saved dozens of emergency submissions I can name.

A large, boring ecosystem. React Native is old enough that the sharp edges have been sanded by a lot of other people. When I hit a problem, someone hit it in 2022 and wrote it up. That was never true of the cross-compilers.

Subscriptions I didn't have to build twice. RevenueCat gives one API for "is this person paid?" across both stores. I've written native subscription handling twice before. I'm not doing it a third time.

Where I'd still go native

When the product is the hardware. Camera pipelines, background location, anything that lives in the OS rather than in a screen. Expo has modules for all of it, and they're fine until they aren't. On one app, a document-capture step that had to work across hundreds of phone models went to the platform's native scanner rather than a custom camera, and getting there meant a config plugin and a rebuild I'd rather have skipped. When the feature is the hardware, native wins. When the hardware is a feature, Expo is fine.

When feel is the product. A React Native app can feel exactly like a native one. It doesn't by default. If the entire value is in how a gesture lands or how a transition breathes, native gets you there with less fighting. Most apps aren't that. The ones that are, know it.

When the team is native. If a client already has Swift and Kotlin engineers, one codebase is a solution to a problem they don't have.

The rule

Expo when the product is the content and the flow. Native when the product is the hardware or the feel.

A study app, a trading game, a benefits directory: content and flow. A scanner, a fitness tracker, a camera app: hardware. Most apps people ask me to build are the first kind, and after dozens of both, I'd build most of them on Expo without much debate.

If you're hiring me

"We need an iOS app" usually means "we need an app, and we have iPhones." Ask whether your users do. If the answer is "mostly," one codebase covers the rest of them for nearly nothing, and it's the version of the project one person can maintain after launch. I've built it the other ways. I'll tell you when yours is the exception.

More notes, or the work itself.
Everything here comes from products that are live right now. Read the rest of the notes or go look at the shelf.
All notes The work →