Inside the RapidNative Editor: How We Built a Browser-Based AI App Builder
By Suraj Ahmed
6th Jul 2026
Last updated: 6th Jul 2026
Building an editor that lets someone type "make the login button blue" and see a running React Native app update in real time — inside a browser — is a strange engineering problem. Traditional IDEs assume a filesystem, a compiler, and a runtime. Our users have none of those. They have a chat box, a canvas, and a preview of an app that has to feel real.
This post is a technical deep dive into the RapidNative editor — the visual surface layer sitting on top of our AI code generation pipeline. We'll cover how a browser-based React Native bundler makes live preview possible, how an iframe sandbox and a postMessage bridge keep the editor and preview in sync, how Redux Toolkit models the editor's state, and how a click on a rendered UI element gets translated into an AI-driven code edit. If you've been curious about what an AI app builder architecture actually looks like under the hood, this is the tour.
A modern AI code editor lives entirely in the browser — no local toolchain required. Photo by Christopher Gower on Unsplash
The Problem: An Editor Without a Local Toolchain
RapidNative's promise is that anyone — founders, designers, product managers — can build a real React Native app without installing Xcode, Android Studio, Node, or Metro. Everything happens in a browser tab.
That constraint cascades into every architectural decision:
- No local filesystem → we need a virtual file system that mirrors an Expo project structure.
- No native compiler → we need a bundler that runs in JavaScript, in the browser, fast enough to feel instant.
- No device runtime → we need to render React Native components on the web, faithfully enough that layout and interactions match a real device.
- No terminal → error recovery, hot reload, and stack traces all have to happen through UI feedback, not console output.
The editor solves each of these with a coordinated stack of Next.js 15, React 18, Redux Toolkit 2.8, an in-browser bundler called almostmetro, and an iframe-based sandbox for the preview. Here's how it all fits together.
The Big Picture
At the highest level, the editor is three coordinated systems:
- The chat + canvas UI — where the user types prompts, sees code, and interacts with a rendered preview.
- The virtual file system + bundler — a Web Worker that turns generated
.tsxfiles into runnable JavaScript bundles. - The AI pipeline — a streaming, multi-LLM system that reads project context and writes React Native code.
These systems communicate through Redux Toolkit as a shared state layer, a MessageManager middleware for iframe postMessage traffic, and Server-Sent Events (SSE) for the AI stream. There's no WebSocket. There's no cloud sandbox. Everything happens in the user's browser and in stateless serverless functions.
The editor is a coordinated set of browser subsystems, not a monolithic app. Photo by Taylor Vick on Unsplash
The Browser Bundler: almostmetro in a Web Worker
The single biggest architectural bet in the RapidNative editor is that Metro — React Native's official bundler — could be replaced with something small enough to run in a browser tab.
We ship browser-metro and @shaper-studio/almostmetro as dependencies. Both are Metro-compatible bundlers that execute entirely in JavaScript, without Node. Together with @babel/standalone for in-browser transforms and acorn + @sveltejs/acorn-typescript for TypeScript parsing, they form a self-contained toolchain that runs inside a Web Worker.
The orchestration lives in src/modules/file/instances.ts and bundler.worker.ts. When a user's code changes, the flow is:
- A Redux action updates the virtual file system (implemented in
src/modules/worker/lib/virtual-file-system/). instances.tssends awatch-updatemessage with the changed files to the worker.- The worker performs an incremental rebuild — recompiling only the changed modules — and posts the updated bundle back.
bundle-html.tswraps the JavaScript in an HTML template, injects shims for NativeWind, Expo, and Reanimated, and creates ablob:URL.- The iframe's
srcis updated to the new blob, or the module is hot-reloaded in place.
The result: a running React Native Web app, updated in under a second on most projects, with no server round trip. Every preview you see in the editor was compiled locally, in your browser, moments before you saw it. This is the piece that makes the AI-generated code feel "alive" instead of feeling like a screenshot.
The iframe Sandbox and postMessage Bridge
Once we have a compiled bundle, we still need to render it somewhere. The answer is an iframe. Not a WebContainer, not an SSE stream — a plain <iframe src="blob:..."> in src/modules/studio/components/custom/editor/SandboxPreview.tsx.
An iframe gives us three things:
- Isolation. Generated code can't reach into the editor's DOM or steal focus.
- A separate JavaScript context. Errors, event loops, and React roots stay contained.
- A message boundary. Anything crossing between editor and preview has to go through
postMessage, which we can validate and version.
The MessageManager in src/modules/editor/client/managers/MessageManager.ts is the traffic cop. It's registered as a Redux middleware at store creation, and it handles every message the iframe sends: selectLayer, hoverLayer, error, route-change, console-log, and more.
Here's a concrete case. When a user clicks an element inside the preview, a script we inject at bundle time (see src/modules/file/layer-selection-script.ts) walks the React fiber tree via the DOM node's __reactFiber$ key, finds a data-bx-path attribute (injected by almostmetro during bundling), and posts a message like:
window.parent.postMessage({
type: 'selectLayer',
payload: {
path: 'app/(tabs)/index.tsx:42:5',
filePath: 'app/(tabs)/index.tsx',
x, y, width, height,
},
}, '*');
The parent editor's MessageManager receives this, dispatches a Redux selectLayer thunk, and the editor state updates: the canvas draws a selection highlight, the Monaco editor scrolls to line 42, and the AI input box becomes context-aware of that specific element. The user never sees the plumbing. They just clicked a button and now it's selected.
Postmessage-based IPC keeps the editor and preview in sync without a network layer. Photo by Christopher Gower on Unsplash
The Redux Layer: Seven Slices, One Message Bus
The editor's state layer uses Redux Toolkit 2.8 with seven slices, each modeling a different domain:
appSlice— Dashboard context, credits, subscriptions, projects. Team-scoped.editorSlice— Zoom, device selection, artboards, selected layer, error overlays, AI-in-progress flag.themeEditorSlice— Design system editor state (colors, fonts, spacing tokens).integrationSlice— External service integrations (Stripe, Supabase, Firebase).recoverySlice— AI error recovery and cascade state.- Plus
counterSlice,projectSlice,fontSlice— legacy slices we're gradually phasing out.
RTK Query handles a narrow set of network calls — mostly comments and share permissions, defined in commentsApi.ts and shareApi.ts. Tag types (Comment, File, ProjectShare, ProjectShareTeamUser) are managed manually rather than through automatic cache invalidation. That's a deliberate choice: the editor's most important state is local (open files, current selection, AI stream), and RTK Query's cache model doesn't fit local-first workflows well.
The store is created in src/modules/editor/store/store.ts, and its middleware stack is where things get interesting:
- Default RTK middleware (thunks, immutability checks).
messageManagerMiddleware— validates that theMessageManageris initialized and routes iframe messages into Redux actions.baseApi.middleware— RTK Query's own middleware.
At store creation, we also initialize four singletons: TimelineEventLog (an in-memory event log for debugging AI-driven changes), RecoveryOrchestrator (which watches for compile errors and can roll back a cascade of AI edits), DogWatcher (a heartbeat check for iframe liveness), and MessageManager itself. Each is retrievable via a helper like getMessageManager(), keeping the singleton pattern testable without leaking into global scope.
Point-and-Edit: How a Click Becomes a Code Change
The single feature that best captures the editor's design philosophy is point-and-edit. Click any rendered element, describe the change you want, and the AI rewrites just that piece. Here's the full flow, end to end:
- Click in the preview iframe. The layer-selection script walks the React fiber tree, finds the
data-bx-pathattribute (which encodesfile:line:col), and posts aselectLayermessage. - MessageManager receives, dispatches to Redux.
editorSlice.selectedLayerupdates. The canvas overlays a highlight box. Monaco scrolls to the corresponding line. - User types a prompt. "Make this button blue."
- Editor builds AI request context. It grabs the selected line from Monaco, marks the target line with a
^^marker, and includes the surrounding code asspecificCodeContext. - The AI request fires. POST to
/api/user/ai/generate-v2with{ query, projectId, specificCodeContext, filePath }. - Two-step LLM pipeline runs on the server. Step 1: a fast model with tool-calling gathers extra project context (grep, file reads, image lookups). Step 2: a larger model — selected dynamically via
getAIModelAsync(modelPurpose)— generates the code change without tool-calling to avoid step exhaustion. - SSE stream to the browser. Each
textchunk arrives, streamed by the Vercel AI SDK'sstreamText(). The editor writes chunks into the virtual file system as they arrive. - The bundler rebuilds. VFS change triggers the Web Worker. HMR updates the iframe module.
- The preview updates. The button is now blue. Elapsed time: usually 3–6 seconds for a single-element change.
Every step is observable. The TimelineEventLog records the AI request, the code diff, and the bundler result, so if something goes wrong, the RecoveryOrchestrator can walk backward and offer a rollback.
Streaming AI Code into a Live Editor
The AI pipeline itself is described in more depth in our post on streaming AI-generated code, but a few editor-side details are worth calling out here.
The /generate-v2/route.ts endpoint (a ~1,700-line file — the beating heart of the AI system) returns an SSE stream with three event types: text (code chunks), usage (token counts), and done (finish reason). The client uses a resilientSSE wrapper that supports stream resumption via streamId and lastEventId, so if a user's network drops mid-generation, the client can reconnect and pick up where it left off. This turned out to be surprisingly important — long generations on mobile networks were failing often enough that a naive EventSource implementation would have been unusable.
Model selection is multi-provider. We ship providers for Anthropic's Claude, OpenRouter, Google Vertex, Azure OpenAI, AWS Bedrock, and xAI, all through the ai SDK's provider abstraction. Which model handles which step is decided per-request by a repository lookup, so we can shift traffic without a deploy.
After the stream ends, Next.js 15's after() primitive kicks off a batch of fire-and-forget work: crediting the user account, logging to Slack for internal observability, and updating the project's last-edited timestamp. Because after() runs post-response, the user sees the code finish streaming before any of that housekeeping starts.
SSE streaming makes AI code generation feel like watching a colleague type. Photo by NASA on Unsplash
Recovery: When the AI Writes Broken Code
Let's be honest: AI-generated code sometimes doesn't compile. The editor is designed around this fact rather than pretending otherwise.
The RecoveryOrchestrator (initialized in the Redux store) subscribes to bundler error events. When the bundler fails to build after an AI change, the orchestrator has three levers:
- Ask the AI to fix its own output. The
broken-screen-alertAPI route ships the error, the file, and a code frame back to the model with a targeted "fix this specific error" prompt. Errors are formatted withbundler-error-parser.ts, which produces stack-trace-style output that models handle well. - Roll back the last change. The VFS supports snapshots (
bundler-checkpoint.ts), and the orchestrator can restore a known-good state. - Escalate to the user. If auto-recovery fails twice, an error overlay appears in the canvas with the option to describe a fix manually.
This closed loop — generate → compile → detect error → auto-fix — is the single biggest quality improvement we've made to the editor in the past year. It turned "the AI writes buggy code" from a blocker into a background nuisance the user rarely notices.
Auth, Persistence, and the Bits People Don't See
The parts of the editor users don't think about are still load-bearing:
- Auth. NextAuth 4.24 with Google OAuth and a custom email OTP provider (6-digit codes stored in an
otp_tokenstable with a 10-minute expiry). The middleware atsrc/middleware.tsalso handles IP allowlisting for admin routes, a maintenance-mode toggle, and site-mode routing between the marketing site and the studio. - Persistence. Supabase (PostgreSQL) with a repository pattern in
src/modules/api/db/. Twenty-five-plus repository classes wrap all queries —ProjectRepository,UserRepository,TeamRepository,FileRepository, and the rest. No ORM. Each method returns Supabase's raw{ data, error }; the caller destructures. This is deliberately unfashionable and it makes debugging queries trivial. - Background jobs. Inngest handles a small set of email sequences (a 2-day / 6-day / 15-day welcome follow-up). No mission-critical work runs in Inngest — the editor is designed to work even when background jobs are lagging.
- File exports. When a user hits "Download project,"
/api/user/projects/[projectId]/download/route.tsusesadm-zipto zip the virtual file system in memory and stream the archive asapplication/zip. The exported code is a plain Expo project — importable straight into any React Native or Expo workflow.
People Also Ask
How does the RapidNative editor render React Native apps in a browser?
Generated React Native code is bundled in-browser by a Metro-compatible bundler (almostmetro) running in a Web Worker, wrapped in an HTML template with React Native Web shims, and served to an iframe via a blob: URL. There's no server-side rendering and no cloud sandbox — the whole preview compiles locally.
Why did we choose Redux Toolkit over Zustand or Jotai for the editor?
The editor has cross-cutting concerns — an iframe message bus, an AI stream, a bundler event stream, a recovery orchestrator — that all need to observe and react to shared state changes. Redux's middleware model gave us a clean place to hang each of those, and RTK Query gave us a pragmatic layer for the handful of network calls that do need caching. Zustand and Jotai are lighter, but neither offers the middleware ergonomics we lean on heavily.
Can I export the code my RapidNative project generates?
Yes. Every RapidNative project is a plain Expo project. The download endpoint zips your virtual file system and gives you a full source tree you can open in VS Code, push to GitHub, or ship to the App Store.
What We Learned Building This
A few takeaways from three years of building this editor that we didn't expect going in:
Local-first was the right call. We considered cloud sandboxes (StackBlitz-style WebContainers, remote VMs). Every time we prototyped a cloud approach, the round-trip latency ruined the "the AI just typed and the preview updated" feeling. Running everything in the browser is harder to build, but the UX is worth it.
Iframe boundaries are underrated. A postMessage bridge is annoying to write and easy to under-document, but it forced us to define a clean protocol between the editor and the preview. Two years in, that protocol is stable, versioned, and boring — which is exactly what you want in a boundary.
The bundler is the product. Users notice bundler speed more than they notice AI speed. A 4-second AI generation followed by an 800ms bundle feels fast. A 2-second AI generation followed by a 3-second bundle feels broken. We spend a disproportionate amount of engineering time on the browser bundler for this reason.
Recovery is a feature. The RecoveryOrchestrator is invisible when things work. It's the single most valuable component when things don't.
Try the Editor Yourself
You've just read a tour of an AI app builder architecture that took several years to converge on. The best way to see whether the choices we made feel right is to open the editor and build something with it. Start with a prompt, sketch a rough screen on the whiteboard, or paste a PRD — the editor treats all of them as entry points into the same generation pipeline.
If you want more architecture posts like this, we've written about why we chose Expo over bare React Native, how our AI understands complex UI layouts, and the four-step LLM pipeline behind our code generation.
Start building for free — no install, no toolchain, just your browser and an idea.
Ready to build your app?
Turn your idea into a production-ready React Native app in minutes.
Free tools to get you started
Free AI PRD Generator
Generate a professional product requirements document in seconds. Describe your product idea and get a complete, structured PRD instantly.
Try it freeFree AI App Name Generator
Generate unique, brandable app name ideas with AI. Get creative name suggestions with taglines, brand colors, and monogram previews.
Try it freeFree AI App Icon Generator
Generate beautiful, professional app icons with AI. Describe your app and get multiple icon variations in different styles, ready for App Store and Google Play.
Try it freeFrequently 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.