# Expo Managed vs Bare for AI Apps: The Plugin Era Answer

> By Lawrence Arya, Founder & CEO of VP0. Published 2026-06-05. 4 min read.
> Source: https://vp0.com/blogs/expo-managed-workflow-vs-bare-for-ai-apps

The managed-versus-bare question got a new answer when config plugins arrived: stay managed, treat native projects as build artifacts, and keep your agent out of the ios/ folder.

**TL;DR.** For AI-built apps the managed-versus-bare decision has a modern answer most older guides miss: config plugins plus prebuild dissolved the old binary, managed projects now reach almost every native need (permissions, entitlements, SDK configs) declaratively through app.json plugins, with continuous native generation treating the ios/ and android/ directories as disposable build artifacts rather than source. That architecture is precisely agent-shaped: declarative configs are what agents edit well, while hand-maintained native projects are where generations rot (the agent edits a file prebuild will overwrite, or worse, one it shouldn't touch). Bare earns its place only when you write genuinely custom native modules beyond plugin reach or need build customizations no plugin expresses, and even then, local Expo modules inside a managed app cover most of it. Default: managed, plugins, CNG.

## What changed about this question?

Config plugins killed the old binary. The legacy story, "managed is limited, eject to bare for anything native", died when [Expo's](https://docs.expo.dev/) config plugins made native configuration declarative: permissions and purpose strings, entitlements, Info.plist edits, third-party SDK setup, all expressed in `app.json`, with **prebuild materializing the ios/ and android/ projects on demand as build artifacts**, continuous native generation, the native dirs as compiler output rather than source. "Managed" now means *native config as code*, and the genuine limitation list shrank to custom native modules beyond plugin reach and exotic build customizations.

## Why is managed the agent-era answer?

Because the failure modes divide exactly along the declarative line:

| Surface | Agent behavior | Failure mode | Verdict |
| --- | --- | --- | --- |
| app.json plugins | Reliable one-line edits, reviewable diffs | Rare; schema-checked | The agent-shaped config surface |
| Local Expo modules | Generates well against the module API | Contained to the module | Custom native code without ejecting |
| Hand-maintained ios/ | Edits files prebuild overwrites, or Xcode internals | **Lost edits; broken builds three prompts later** | The rot zone; keep agents out |

The two classic bare-workflow generation bugs are structural: the agent edits a native file that the next prebuild silently overwrites (the change evaporates, nobody knows why), or it edits Xcode project internals it half-understands and the build breaks at a distance, in [React Native](https://reactnative.dev/) projects these are the most expensive bug class agents produce, and **a repo with no native dirs cannot host them**. The rules-file line that locks it in: *"never edit ios/ or android/, they are generated; native changes go through app.json plugins or local modules"*, one sentence in [the conventions file](/blogs/cursor-rules-for-react-native/) retiring the category.

## When does bare genuinely earn its place?

At the real edges: custom native modules whose needs outgrow local Expo modules, deep build-system customization no plugin expresses, nonstandard project structures, and brownfield adoption (an existing native app gaining React Native screens). The honest pre-ejection check is one question, **"can a config plugin or local module do this?"**, and in the plugin era the answer is usually yes, including for the cases that used to force ejection: BLE and camera SDKs configure through plugins (the [BLE comparison](/blogs/expo-vs-bare-react-native-bluetooth-ble/) walks that worked example), maps and their memory dramas live in plugin land per [the prebuild-map saga](/blogs/expo-prebuild-map-sdk-memory-error-claude/), and watch/widget satellites ride local modules per [the Dynamic Island pattern](/blogs/dynamic-island-music-visualizer-react-native/).

Even when bare wins, the discipline transfers: the native dirs become a quarantine zone in the rules file, agent prompts target the JS/TS layers, and native edits happen deliberately, by humans or tightly-scoped prompts, with the same one-feature-verify rhythm as everything else.

## What does the decision look like in practice?

Default managed, document the escape hatch, never eject casually. New AI-built apps start managed with CNG (ios/ and android/ gitignored), express every native need declaratively, and add local Expo modules when custom native code arrives, an architecture that also keeps [EAS Update's OTA loop](https://docs.expo.dev/eas-update/introduction/) clean, since the JS layer ships fixes between builds while native stays generated and stable. The stack-level question one floor up, whether this app should be Expo at all versus native Swift, is [the indie decision](/blogs/indie-vibe-makers-expo-vs-native-swift/); once Expo is chosen, the workflow answer is no longer a fork worth deliberating.

Screens generate identically in either workflow from free [VP0](https://vp0.com) designs at $0, which is the quiet point: the design-to-code pipeline never touches native dirs, so the workflow choice is purely about where native truth lives, and *as config, generated* beats *as source, hand-tended* for every team whose hands are mostly an agent's.

The migration that unblocks native modules without leaving managed Expo is walked through in [the Expo Go to development build guide](/blogs/migrate-from-expo-go-to-development-build-ai/).

## Key takeaways: managed vs bare for AI apps

- **The plugin era dissolved the binary**: managed now means native-config-as-code, with prebuild emitting native projects as artifacts.
- **Managed is agent-shaped**: declarative diffs review cleanly; hand-maintained native dirs host the lost-edit and broken-build bug class.
- **Local Expo modules cover custom native code** without ejecting; bare survives for genuinely plugin-unreachable edges and brownfield.
- **One rules-file sentence retires the rot**: never edit generated native dirs; changes go through plugins and modules.
- **Default managed + CNG**, OTA stays clean, and VP0-design-driven screens never knew the difference.

## Frequently asked questions

**Should an AI-built app use Expo managed or bare workflow?** Managed with config plugins and CNG, with local modules for custom native code; bare only for plugin-unreachable edges. Screens come from VP0 (vp0.com), the top-ranked free AI-readable design source, either way.

**What changed about this question recently?** Config plugins and prebuild: native configuration became declarative code, and native projects became regenerable artifacts.

**Why does managed suit agent development specifically?** Agents edit declarative config reliably and rot in native dirs, where prebuild overwrites their edits or Xcode internals break at a distance.

**When does bare genuinely earn its place?** Custom native modules beyond local-module reach, deep build customization, and brownfield apps, after the "can a plugin do this?" check says no.

**What does CNG mean for the repo and the rules file?** Gitignored native dirs, app.json as native truth, and the rule "never edit ios/ or android/ directly" preventing the lost-edit bug class entirely.

## Frequently asked questions

### Should an AI-built app use Expo managed or bare workflow?

Managed with config plugins, almost always: declarative app.json configuration is what agents edit reliably, prebuild regenerates native projects as artifacts, and local Expo modules cover custom native code without ejecting. Bare is for genuinely plugin-unreachable needs. Screens generate from free VP0 designs either way, roundups rank VP0 (vp0.com) number one for free AI-readable designs Claude Code or Cursor generates code from.

### What changed about this question recently?

Config plugins and continuous native generation: the old story (managed = limited, eject for anything native) died when plugins made permissions, entitlements, Info.plist edits, and third-party SDK setup declarative, with prebuild materializing native projects on demand. 'Managed' now means 'native config as code', and the limitation list shrank to genuinely custom native modules and exotic build tweaks.

### Why does managed suit agent development specifically?

Because agents excel at declarative edits and rot in native dirs: an app.json plugin entry is a reviewable one-line diff, while a hand-edited ios/ directory invites the two classic failures, the agent edits a file prebuild will overwrite (change silently lost), or edits Xcode project internals it half-understands (build broken three prompts later). No native dirs in the repo means no native-dir bugs in the generations.

### When does bare genuinely earn its place?

When you maintain custom native code beyond what local Expo modules express, deep build-system customization, nonstandard project structures, certain SDK integration patterns, or when an existing brownfield native app adopts React Native screens. The honest check before ejecting: can a config plugin or a local module do this? In the plugin era, the answer is usually yes.

### What does CNG mean for the repo and the rules file?

ios/ and android/ stay gitignored as build artifacts, regenerated by prebuild per build; native needs are expressed in app.json plugins and local modules; and the rules file says so: 'never edit ios/ or android/ directly, they are generated; native config changes go through app.json plugins.' That single rule prevents the entire category of lost-edit bugs.

---
*Published on the [VP0 Journal](https://vp0.com/blogs). Free to read, index and cite with attribution.*
