React Native
On Android and iOS your C++ compiles to native libraries inside the app's own Gradle and Xcode builds, and JavaScript calls them over JSI: no WebAssembly and no SharedArrayBuffer. You import a header from a component, as on the web, and call it after initNative().
Set up
Install the runtime packages as dependencies, so React Native autolinks them, and the Metro plugin as a dev dependency. crossbind itself comes with @crossbind/plugin-react-native.
Builds need Docker to generate the bridges for both platforms, or a remote runner, and the mobile tools in Prerequisites. Add a crossbind.config.mjs at the project root; a port joins with its -android and -ios packages, both installed and listed:
Your own C++ goes under src/native. Import its header from a component and call it once initNative() resolves; here Native is a class declared in src/native/native.h:
To start from a template instead: npm create crossbind@beta -- my-app Mobile "React Native" "Native CLI", or Expo as the last argument.
Metro
@crossbind/plugin-metro turns header, .i and .rs imports into JavaScript modules and writes the C++ bridge for each header while Metro bundles. Wrap the default config with it:
In a monorepo, also add the workspace root to watchFolders, or Metro cannot resolve the workspace packages.
iOS
iOS libraries build on the Mac itself, with the Xcode at /Applications/Xcode.app, for iOS 15.1 and later. Install the pods, then run the app:
pod install compiles your C++ and its bridges into the xcframework of the react-native-crossbind pod, and a crossbind build phase rebuilds what changed before every Xcode build. Each port arrives as a prebuilt pod of its own. Run pod install again when conanDependencies change.
crossbind build phase runs node by name, from Xcode's environment rather than your shell's. If it is not found there, link it once: ln -s $(which node) /usr/local/bin/node.Android
There is no separate step: npm run android starts Gradle, whose crossbind tasks write the bridges and stage the ports' data files before preBuild, and whose CMake build compiles your C++ for each ABI.
- ABIs:
arm64-v8aandx86_64, narrowed to the app'sreactNativeArchitectures. - CMake 3.28 or newer, which the plugin's CMake script requires.
- Your C++ compiles with the app's NDK at its
minSdkVersion, 24 when the app sets none. The port libraries come prebuilt from crossbind's Android image, built against API level 33.
Expo
Expo Go ships a fixed native binary and cannot load your C++, so the app needs native projects of its own, which expo prebuild generates. Install the same packages as above, and add the config plugin to app.json before prebuilding:
During prebuild it drops the x86_64 simulator architecture from every pod and the app target, raises any pod below iOS 15.1 to 15.1, and adopts the UIScene lifecycle iOS 27 requires, unless the app already declares a scene manifest. Wrap Expo's own Metro config rather than @react-native/metro-config:
The web build compiles the same imports to WebAssembly: the browser build, single-threaded unless target.runtime is 'mt', which then needs COOP/COEP headers from the host. Metro resolves imports against the files it found at startup, so stage the web dependencies first; an export also needs the wasm linked in after Expo bundles:
Threads and files
Android and iOS builds are always multithreaded: C++ threads are the device's own, with no SharedArrayBuffer or COOP/COEP headers involved, and useWorker does not apply. target.runtime changes only the Expo web build; Threads and workers covers that side.
C++ opens real files in the app's sandbox, so there are no /opfs or /memfs mounts: give it paths your app already uses. Files covers the other platforms.
Release builds
The C++ compiles inside the app's own build, so a release build needs no extra step:
Limits
- React Native 0.81 or newer: the bindings keep JavaScript callbacks in JSI runtime data, which 0.81 added.
- iOS simulator builds are arm64 only, so they need an Apple silicon Mac.
- A port's header binds only the functions the app imports. Metro sees a new import as soon as the file is saved, but the app has the function only after its next native build; until then the call fails with
... is not bound in the native module.
examples/mobile-reactnative-cli and examples/mobile-reactnative-expo are complete apps.