Why We Chose Expo Over Bare React Native for AI Code Generation

RI

By Riya

26th Jun 2026

Last updated: 26th Jun 2026

Why We Chose Expo Over Bare React Native for AI Code Generation

When you're building an AI that writes React Native code on behalf of non-developers, the first architectural decision isn't which model to use. It's which flavor of React Native the model is allowed to generate.

We picked Expo. Not Expo as a starting point that we'd graduate from. Expo all the way down — managed workflow, Expo Router, EAS Build, Expo Go for previews, the whole tooling story. The AI is not even given the option to write bare React Native code. Our system prompt prohibits it.

That choice wasn't obvious in 2023. In 2026 it's the only choice that makes sense for AI-driven app generation, and the reasoning has very little to do with "Expo is easier." It has to do with reducing the AI's decision space, eliminating entire categories of hallucination, and making the gap between "generated code" and "running app on a real phone" effectively zero.

This post explains every reason — with the actual constraints we wrote into our prompts, the actual packages we ship, and the failure modes we sidestepped.

A developer working on a React Native app with a phone in hand Photo by Daniel Korpai on Unsplash

Expo for AI code generation: the 60-second answer

Expo for AI code generation is the practice of constraining a large language model to emit code that runs on the Expo SDK and Expo Router rather than bare React Native. This shrinks the model's decision space, removes ambiguous choices (which navigator? which file-system library? which build tool?), and lets the generated app run on a user's phone in seconds via Expo Go — no Xcode, no Android Studio, no native build configuration. It is the difference between "code that probably compiles" and "an app the user can hold in their hand 30 seconds after the AI finishes streaming."

The rest of this post walks through the eight reasons we made this call, the trade-offs we accepted, and what the RapidNative codebase actually does to enforce it.

The problem with giving AI both options

Bare React Native gives you flexibility. You pick your navigation library (React Navigation? React Native Navigation? something else?). You pick how you handle deep links, file system access, fonts, splash screens, camera, secure storage. You configure Xcode and Android Studio. You write your own MainApplication.kt and AppDelegate.swift when something needs to be wired natively.

A human team picks each of those things once, documents it, and moves on. An AI that generates a new app every few seconds picks them again every time — and gets them wrong in subtly different ways.

We saw this empirically. Early prototypes of our generator gave the model freedom over routing. It would generate a screen using React Navigation in one project, switch to deep imports from expo-router in the next, and occasionally invent a hybrid that doesn't exist. The model wasn't broken. It was responding to the fact that its training data contains every possible React Native pattern, weighted roughly by how often each appears on GitHub. With no constraint, it averages over the chaos.

The fix is to remove the choice. In our current system prompt at src/modules/api/services/ai/prompts/system-prompts.ts, the relevant section reads:

Allowed: Use basic navigation via router.push() (from useRouter()) or the Link component from expo-router only. Prohibited: Do not use useSearchParams, useLocalSearchParams, useGlobalSearchParams, usePathname, or any other dynamic routing hooks.

That's not a recommendation. It's a hard import allowlist. The model is told exactly which two ways to navigate, and any output that violates the rule is regenerated. The cost of this rigidity is that some advanced routing patterns aren't available to the AI. The benefit is that the routing code is correct, every time, in every generated app.

Reason 1: file-based routing matches the LLM's mental model

LLMs are extraordinarily good at file-based routing. They've seen Next.js routing in roughly half the React code on the internet. They know what app/profile.tsx means, they know what app/(tabs)/home.tsx means, and they get the conventions right without being reminded.

Expo Router 6 uses the same file-based routing model as Next.js App Router. The directory becomes the route tree. app/_layout.tsx is the layout. app/(group) is a route group. A model that has seen ten million Next.js apps in training does not have to learn anything new to write Expo Router code correctly.

Compare that to React Navigation, which is configuration-driven:

const Stack = createNativeStackNavigator();
<Stack.Navigator>
  <Stack.Screen name="Home" component={HomeScreen} />
  <Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>

It's not hard. But it's a graph the AI has to build, hold in memory, and reference consistently across files. When the model gets it wrong, the navigation breaks in ways that surface only at runtime, not at type-check.

Expo Router's file-based routing is essentially declarative — the file system is the navigation graph. The model doesn't have to track state across files; it just has to pick the right path for the new screen. That distinction is invisible to a human developer who only writes one app a week. To an AI that generates thousands of apps a day, it's the difference between a low-error pipeline and a high-error pipeline.

A laptop showing a code editor with mobile app development Photo by Florian Olivo on Unsplash

Reason 2: the dependency surface is curated, not infinite

Pick a feature an app needs: secure local storage, camera access, font loading, image caching, push notifications, deep linking. On bare React Native, each of those is a category with five plausible npm packages, each with different maintenance status, different APIs, and different native build requirements.

If we let the AI choose, it would pick whatever was popular in 2023, which is now half-abandoned. We'd have to manually maintain a "blessed package list" and rewrite the model's outputs to use it.

Expo solves this by shipping a curated SDK. Our generated apps use a tight, stable set of Expo packages that we know work together:

  • expo-image — image rendering and caching
  • expo-image-picker — camera and photo library
  • expo-file-system — file I/O
  • expo-sqlite — local database
  • expo-secure-store (when needed) — keychain/keystore access
  • expo-linking — deep links
  • expo-font — custom fonts
  • expo-splash-screen — launch screen
  • expo-status-bar — status bar styling
  • expo-symbols — SF Symbols on iOS
  • expo-linear-gradient — gradient backgrounds
  • expo-web-browser — in-app browser
  • expo-constants — runtime constants

When the AI needs to add camera access, it knows there's exactly one choice: expo-image-picker. It doesn't pull in react-native-image-picker, react-native-vision-camera, or a randomly hallucinated @somebody/react-native-camera-2024. The decision space is closed, and the model writes code against a known-good API.

This is the most underrated benefit of Expo for AI code generation. The model isn't just generating syntax. It's choosing dependencies. A curated SDK turns dependency selection from an open-ended search problem into a lookup.

Reason 3: zero setup between "AI finished writing" and "app on my phone"

If a user describes an app to RapidNative and waits 90 seconds for the AI to generate it, that user has not earned the right to then install Xcode, configure CocoaPods, accept five license agreements, plug in a USB cable, and discover their iOS version is too old. The instant the AI finishes, the app needs to be runnable.

Expo's tooling makes this possible. The generated project is structured so that on a fresh machine, npm install and npx expo start are the only two commands separating you from a QR code you can scan with Expo Go. No native build. No Xcode. No Android Studio. The JavaScript bundle loads onto Expo Go and the app renders on the actual hardware in your hand.

For a non-developer using RapidNative, this collapses further: we host the preview in the browser, and a QR code lets them open the same code on their phone in seconds. The marketing copy ("Test on real devices via QR code") is not aspirational — it's a direct consequence of the fact that Expo Go can run any Expo SDK 54 app without compilation.

You cannot do this with bare React Native. There is no equivalent universal runtime. Bare apps require a build, which requires a toolchain, which requires a Mac (for iOS), which requires Apple developer credentials for device deployment. Every one of those steps is a place where a non-developer abandons the product.

A smartphone showing a mobile app interface Photo by Yura Fresh on Unsplash

Reason 4: managed services replace the parts AI can't write

The hard part of shipping a mobile app isn't writing the screens. It's the surrounding infrastructure: signing certificates, provisioning profiles, build artifacts, store submission, over-the-air updates, crash reporting, push notification servers.

An AI can write a screen. An AI cannot create your Apple Developer account, generate an APNs key, configure entitlements, or upload a build to App Store Connect on your behalf. Those steps require credentials, browser sessions, and decisions only a human can make.

Expo's managed services — EAS Build, EAS Submit, EAS Update — bridge this gap. The AI generates the app. The user authenticates with Apple/Google once. EAS handles the rest. Over-the-air updates mean that when the user makes another change in RapidNative, the new code can roll out to TestFlight or to installed dev builds without a new submission.

Bare React Native has no equivalent. You can replicate parts of EAS with Fastlane, CodePush, App Center, and a lot of glue. But that glue is precisely the kind of bespoke configuration that AI cannot reliably author and a non-developer cannot maintain.

This is why the export pipeline we built specifically targets the Expo + EAS combination — a downloaded RapidNative project drops directly into eas build and eas submit without modification.

Reason 5: continuous native generation kills the old escape hatch

The historical argument for bare React Native was: "What if I need a native module that Expo doesn't support?" In 2023 that was a real concern. The answer was either "eject from Expo" (lose managed updates forever) or "live without the feature."

Continuous Native Generation (CNG), introduced in Expo SDK 50 and now standard, eliminated that trade-off. The android and ios directories are no longer hand-maintained. They're regenerated from app.json on every build, with config plugins describing exactly what native customization is needed. You can drop in any native module — including ones Expo doesn't ship — without abandoning the managed workflow.

For our AI generator, CNG matters in two ways. First, it removes the only honest reason a sophisticated user could complain about Expo's limitations. Second, it means the generated android/ and ios/ directories are not part of the project the AI has to reason about. The AI generates JavaScript and config. EAS generates the native layer. The model's surface area shrinks again.

Reason 6: web preview is a free side effect

RapidNative's editor shows a live preview of the user's app in the browser. Implementing that on top of bare React Native is a fight — bare React Native has no first-class web target.

Expo, by contrast, supports React Native for Web natively. Expo Router has a web target. The same component code that renders on iOS and Android renders in a browser canvas. We extend this further with a small internal bridge (tools/rapidnative-expo-router/) that maps Expo Router's API to React Router for web rendering. The AI writes one app; the user sees it in the browser, then scans a QR code and sees the same app on their phone.

If we'd built on bare React Native, the in-browser preview would have required its own renderer, its own routing layer, and a translation layer between what the AI wrote and what the preview displayed. Three separate failure modes, three separate maintenance burdens.

A team collaborating on app design Photo by Annie Spratt on Unsplash

Reason 7: the AI's prompts get shorter

Every constraint you remove from a system prompt is a constraint you don't have to spend tokens explaining. Our prompt for the NativeWind template starts with:

You work inside a pre-configured Expo project with NativeWind v4 and Tailwind CSS already set up. Navigation uses Expo Router (file-based routing).

That's it for environment context. The model doesn't need to be told to configure Babel, install React Navigation, set up react-native.config.js, or wire up Metro. The Expo project ships with all of that pre-configured. The prompt focuses on what the model should do (build the screen the user asked for), not what the environment is.

Shorter prompts mean cheaper inference, faster generation, and less room for the model to get distracted by environmental concerns. They also mean the prompt itself can evolve faster — when you change the environment in a small way, you don't have to rewrite three paragraphs of setup instructions.

Reason 8: NativeWind closes the styling gap

A subtle reason: NativeWind (Tailwind CSS for React Native) is much easier to integrate with Expo than with bare React Native. The Babel and Metro plugins, the CSS-in-JS runtime, and the JSX transformations all assume the standard Expo project structure.

NativeWind matters for AI code generation because LLMs are excellent at Tailwind. The training data is saturated with Tailwind utility classes. Asking a model to style with Tailwind utilities yields visually consistent, well-spaced UI on the first attempt. Asking it to write StyleSheet.create({ ... }) objects with arbitrary numeric values yields a mess.

So the practical stack ends up: Expo (project framework) + Expo Router (file-based navigation) + NativeWind (Tailwind styling) — and each piece is dramatically better in combination with the others than it would be on bare React Native.

What we gave up

Honesty matters. There are three things we genuinely sacrificed by choosing Expo-only:

Some niche native modules — A small number of third-party native modules don't yet have Expo config plugins. For those, you'd need to author a plugin (which our generator doesn't do). In practice this never affects the apps a non-developer wants to build.

Bundle size for trivial apps — The Expo SDK is heavier than a minimal React Native template. For a true "hello world," bare wins by a few megabytes. For any real app, the difference is rounding error.

Total customization of native code — If you absolutely need to fork AppDelegate.swift and ship custom Objective-C, you'd be happier in bare. RapidNative users are not in that situation.

These are real costs. They're just not costs that apply to our users.

How this maps to the RapidNative architecture

Concretely, here's what enforcement looks like in our codebase:

LayerWhat's enforcedWhy
System promptexpo-router only; React Navigation prohibitedEliminates the "which navigator?" decision branch
Import allowlistCurated Expo SDK packages onlyPrevents hallucinated or abandoned npm dependencies
Scaffold (tools/project-templates/fullstack/)Expo 54.0.13 + Expo Router 6.0.12 + NativeWind 4.2.1Ships a known-good environment to every generated project
Preview bridge (tools/rapidnative-expo-router/)Aliases expo-router to React Router on webLets the same generated code render in the in-browser preview
Export route (src/app/api/user/projects/[projectId]/download/route.ts)Produces a runnable Expo projectUser clones, npm install, npx expo start — done

The pieces reinforce each other. The system prompt would be impossible to enforce without the scaffold; the scaffold would be useless without an export path; the export path would be useless without managed services to ship the resulting app. The entire pipeline is Expo-shaped because that's the only way the pieces fit together.

What this means if you're building AI code generation yourself

If you're designing an AI that emits code in any framework — not just React Native — the lesson generalizes. The frameworks that win for AI code generation are the ones with:

  1. Small, curated dependency surfaces — fewer plausible choices means fewer hallucinations.
  2. Convention-over-configuration design — file-based routing beats config-based routing because the model has less state to track.
  3. First-class managed tooling — the parts the AI can't write (auth, signing, distribution) need a vendor to handle them.
  4. Fast feedback loops — if "generated code" to "running on real hardware" takes more than 30 seconds, your users won't iterate.

Bare React Native fails on all four. Next.js succeeds on all four for web. Expo succeeds on all four for mobile. That's the entire story.

Try it

If you want to see what AI-generated Expo code looks like in practice, the easiest way is to describe an app to RapidNative and watch the editor stream the code into a live preview. The generated project is downloadable — you'll get a real Expo SDK 54 project that runs in Expo Go and builds with EAS. No setup. No bare workflow. No regrets.

For more on the architecture, the 4-step LLM pipeline post goes deep on the model orchestration that produces this code, and the multi-LLM post covers why different models handle different parts of the generation.

The short version: the framework you choose for AI code generation is part of the model. Choose accordingly.

Start now

Ready to build your app?

Turn your idea into a production-ready React Native app in minutes.

Free tools to get you started

Questions

Frequently asked questions

What is RapidNative?

RapidNative is an AI-powered mobile app builder. Describe the app you want in plain English and RapidNative generates real, production-ready React Native screens you can preview, edit, and publish to the App Store or Google Play.

Can I export the code?

Yes. RapidNative generates clean React Native and Expo code that you can export at any time. No lock-in, no proprietary format. Hand it to your developers or keep building inside RapidNative.

Is RapidNative free to use?

Yes. You can build apps on the free plan with no credit card required. Paid plans unlock unlimited AI generations, code export, and direct publishing to the App Store and Google Play.

Do I need to know how to code?

No. Most users build apps by describing what they want in plain English. Developers can drop into the code whenever they want more control, but coding is optional.

How long does it take to build an app?

Most users have a working first screen in under a minute. A full MVP usually takes a few hours instead of the weeks or months traditional development requires.