RORK LABJP
TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the roundTOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Articles/Dev Tools
Dev Tools/2026-05-02Intermediate

Adding Native Modules to Rork-Generated Apps: A Practical Guide to Expo Prebuild

When your Rork prototype needs a native SDK or custom module, Expo Prebuild is the bridge to production. This practical guide walks through the limits of Managed Workflow and the actual commands for moving toward Bare Workflow.

Rork515Expo PrebuildNative Modules5React Native209EAS Build14Config Plugin3

You shipped a prototype with Rork, and then production goals start showing up: "I need the full Stripe Mobile SDK," "I need to talk to a custom BLE device," or "We need to integrate our internal SDK." That is the moment Expo Prebuild enters the picture.

This guide walks through how to safely add native code to a Rork-generated Expo Managed Workflow app using Prebuild. Drawing from the times I have crossed this boundary while shipping personal projects, I will also share the gotchas that tend to consume the most hours.

Recapping the Rork–Expo Relationship

Rork outputs an Expo Managed Workflow project — app.json and package.json at the center, and no ios/ or android/ directories. Native builds happen on EAS (Expo Application Services), which is wonderful for the first few weeks but becomes restrictive once you outgrow the expo- library catalog.

When that happens, you have three escape hatches:

  • Stay declarative with a Config Plugin for cases where only native configuration changes (permission strings, Info.plist, AndroidManifest)
  • Prebuild and add a third-party native module distributed through npm
  • Prebuild and write your own native module in Swift or Kotlin

In my experience, jumping straight to native code is rarely the fastest path. Confirming whether a Config Plugin would already be enough almost always pays off.

Things to Check Before Reaching for Prebuild

Once you commit to native code, going back is possible but expensive in maintenance time. Walk through this short checklist first:

  • Is the feature already covered by an Expo first-party library (expo-camera, expo-notifications, etc.)?
  • Is there a third-party library shipped through the Expo Modules ecosystem that solves it?
  • If the change is only native configuration, can a Config Plugin do the job?
  • Is native code truly required, or can a different architecture (WebView, server-side processing) deliver the same outcome?

When the honest answer is "yes, native is required," that is when Prebuild becomes the right tool.

Core Prebuild Commands

Prebuild generates ios/ and android/ directories from app.json. Rather than a one-time eject, it embraces "Continuous Native Generation" — your native projects are reproduced from configuration on every run.

# 1. Snapshot the project (important)
git add . && git commit -m "Snapshot before prebuild"
 
# 2. Align SDK versions if needed
npx expo install --check
 
# 3. Run prebuild (clean rebuild recommended)
npx expo prebuild --clean
 
# 4. Install iOS dependencies
cd ios && pod install && cd ..
 
# 5. Verify locally
npx expo run:ios   # or npx expo run:android

The --clean flag deletes existing ios/ and android/ before regenerating, which means any hand edits you made to native files will be wiped. That is the single most common pitfall — see the next section for how to make changes survive.

A Concrete Example: Adding a Native Module

Imagine adding a library that requires native build steps, such as react-native-mlkit-ocr:

# 1. Install the library
npx expo install react-native-mlkit-ocr
 
# 2. Re-run prebuild so native dependencies are picked up
npx expo prebuild --clean
 
# 3. Refresh CocoaPods
cd ios && pod install && cd ..
 
# 4. Verify the build
npx expo run:ios

The non-obvious bit is step 2: skipping prebuild --clean after installing the library means the Podfile never picks up the new dependency, and you end up staring at a "Native module cannot be found" error. I lost about half an hour to this once before it became muscle memory.

Two Ways to Make Native Changes Persistent

Because prebuild --clean regenerates the native directories, anything you edit by hand in Info.plist or AppDelegate.swift will vanish. Two strategies handle this cleanly.

Strategy 1: Manage Changes Declaratively with a Config Plugin

A Config Plugin rewrites native files based on app.json. To add a camera usage description to Info.plist, write the plugin like this:

// plugins/with-camera-usage.js
const { withInfoPlist } = require('@expo/config-plugins');
 
module.exports = function withCameraUsage(config) {
  return withInfoPlist(config, (cfg) => {
    cfg.modResults.NSCameraUsageDescription =
      'We use the camera to scan business cards.';
    return cfg;
  });
};

Register it in app.json:

{
  "expo": {
    "plugins": ["./plugins/with-camera-usage"]
  }
}

Now prebuild --clean will produce the same configuration every single time.

Strategy 2: Commit to Bare Workflow

If your native changes are extensive, you can stop running prebuild, commit ios/ and android/ to your repo, and operate as a Bare Workflow project. The cost is real merge work whenever you upgrade Expo SDK, so personally I push to keep everything inside Config Plugins for as long as possible.

Pairing Prebuild with EAS Build

After prebuilding, you can build locally — but EAS Build typically removes more friction. A starting eas.json looks like this:

{
  "cli": { "version": ">= 5.0.0" },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "preview": {
      "distribution": "internal",
      "ios": { "simulator": true }
    },
    "production": {
      "autoIncrement": true
    }
  }
}

Build commands:

# Development build (for on-device testing)
eas build --profile development --platform ios
 
# Production build with TestFlight upload
eas build --profile production --platform ios --auto-submit

EAS Build runs Prebuild itself before the actual build, so you do not have to prebuild locally just for CI. You only need to prebuild on your machine when you want to use expo run:ios directly.

Common Pitfalls and How to Handle Them

Three issues I keep seeing in practice:

1. pod install fails because of an Xcode version mismatch

CocoaPods is sensitive to your Xcode version and deployment target. When the platform :ios, '15.1' line in Podfile and the minimum iOS each library demands disagree, you get an install failure. Setting image: latest on EAS Build keeps the cloud environment on the most recent Xcode, which minimizes the local–CI gap.

2. expo-router stops working after Prebuild

Rork-generated apps lean heavily on expo-router, and rewriting AppDelegate during Prebuild can change the initialization order, leaving you with a blank screen. The fix is usually to make sure expo-router is in the plugins array of app.json, which lets @expo/config-plugins apply the proper patches automatically.

3. Android complains it cannot find com.facebook.react:react-native

This usually means android/build.gradle is missing maven { url 'https://www.jitpack.io' } under allprojects.repositories. Either add it via a Config Plugin (withProjectBuildGradle) or follow the library's setup instructions to fix the repository list.

Reading the Generated AppDelegate Helps More Than You Think

One habit I have picked up: the first time I prebuild a project, I open ios/{ProjectName}/AppDelegate.swift and just read it. Modern Expo Prebuild generates a fairly compact AppDelegate that delegates most work to ExpoAppDelegate. Spending five minutes there clarifies several things at once:

  • Where push notification tokens are forwarded
  • How URL handlers route between expo-linking and other plugins
  • What happens during background fetch entry points
  • How ExpoReactNativeFactory initializes the bridge

When something later goes wrong — push notifications never reaching the JS layer, or a deep link not opening the right screen — you already have a mental map of where to put a breakpoint. Without that mental map, debugging native issues from inside a Rork-style workflow can feel like guessing.

The same logic applies to android/app/src/main/java/.../MainApplication.kt. The generated code is not magic; once you have read it once, the rest of the project stops feeling opaque.

Keeping Prebuild Reproducible Across Machines

A practical detail that comes up the moment you collaborate or move CI: native build outputs depend on the exact versions of Xcode, Node, CocoaPods, and Ruby installed locally. Pinning these makes Prebuild reliable.

# .nvmrc
20.11.1
 
# .ruby-version
3.2.2
 
# In Podfile (already set by Expo, but verify)
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'

For the Xcode version on EAS Build, set it in eas.json:

{
  "build": {
    "production": {
      "ios": { "image": "latest" }
    }
  }
}

Once these versions are stable, "works on my machine but not on CI" stops happening as often. That alone has saved me more debugging time than any other configuration choice.

Going Deeper

When you hit specific errors while wiring up native modules, Rork Max + EAS Build Custom Native Module Errors: Complete Resolution Guide catalogs the common patterns and how to recover from each.

A Suggested Next Step

Expo Prebuild is the most important bridge between "a prototype that runs in Rork" and "a native app ready for production." Rather than starting with a large SDK integration, I recommend just running expo prebuild --clean and reading through the generated ios/ and android/ folders. That single act makes the difference between Managed and Bare Workflow tangible in a way no documentation quite manages.

From there, the habit to build is treating every native change as a Config Plugin contribution. Do that consistently and your Rork app gains a foundation that will keep working as the project grows.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Dev Tools2026-05-06
Adding Expo Dev Client to Your Rork App — The First Move When Native Modules Stop Working in Expo Go
The moment you add react-native-mmkv or RevenueCat to a Rork app, Expo Go stops launching it. Here's how to set up Expo Dev Client (a custom development build) and the three pitfalls I've actually walked into.
Dev Tools2026-06-29
Rork × Vision Camera v4: A Production-Grade Camera Stack with QR, OCR, and ML Inference
Wire React Native Vision Camera v4 into a Rork project end-to-end — frame processors, ML inference, QR/OCR, plus battery and thermal tuning, a real-device test matrix, store review, and a staged rollout.
Dev Tools2026-05-11
Rork vs Expo CLI: What the Build Experience Actually Feels Like — A 12-Year Indie Dev Perspective
A hands-on comparison of Rork and Expo CLI build workflows from a developer with 12 years of indie app experience and 50M+ cumulative downloads. Honest take on where Rork creates friction for CLI-savvy developers — and where it genuinely wins.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →