npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-cross-native

v0.1.0

Published

CrossNative - run compiled languages off the JS thread via JSI + WASM

Downloads

1,127

Readme

react-native-cross-native

Run compiled languages off the JavaScript thread in React Native.

React Native runs everything on one JS thread, so heavy work (matrix math, image processing, crypto, simulation) freezes the UI. CrossNative lets you write that work in Rust, Go, Zig, C, or C++, keep it in your project as a plain source file, and call it from JS. It runs on a worker thread, so the UI stays responsive — and it's dramatically faster than the same code in JavaScript.

0.1.0 — first stable release.

Benchmark

The same Monte Carlo π estimate (100 million iterations, an identical LCG in every language) run from one React Native app. Native languages run off the JS thread; JavaScript runs on it and freezes the UI while it works.

Every language returns the same answer (π ≈ 3.14189). The native languages finish in ~120–220 ms; the identical algorithm in JavaScript (Hermes) takes 11–17 seconds — roughly 50–130× slower — and blocks the UI thread the whole time, while the native runs keep it live.

How it works

  1. Compile on bundle. The Metro transformer sees an import x from './f.rs', runs the real compiler (cargo/go/zig) once, and hands your JS a compiled WebAssembly module — you never run a toolchain by hand.

  2. Run off the JS thread. A JSI native module executes the module on a worker thread pool (WebAssembly Micro Runtime), so a long call never blocks the UI.

  3. Native speed on device. By default your module runs on a small, portable WebAssembly interpreter — it works everywhere and is fine for moderate work, but it's slower than real machine code. To reach full speed the module has to become native machine code for the device, and how that happens differs by platform, because the two operating systems have different rules:

    • Android lets an app run code that was compiled after the app shipped, so CrossNative compiles your module ahead of time (AOT) into native code the device runs directly.
    • iOS does not allow an app to generate machine code at runtime, so instead your module is compiled into the app as a static library at build time and called directly.

    Both reach the same native speed — the step-by-step for each is in the two platform guides below.

Setup (both platforms)

These four steps get a module running off the JS thread on either platform.

1. Install

npm install react-native-cross-native
cd ios && pod install && cd ..     # iOS only

One package is all you need — it pulls in the compiler and core automatically.

2. Configure Metro — add the transformer and source extensions to metro.config.js:

const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
const defaultConfig = getDefaultConfig(__dirname);

module.exports = mergeConfig(defaultConfig, {
  transformer: {
    babelTransformerPath: require.resolve(
      'react-native-cross-native/metro-transformer',
    ),
  },
  resolver: {
    sourceExts: [
      ...defaultConfig.resolver.sourceExts,
      'rs', 'go', 'zig', 'c', 'cc', 'cpp', 'cxx',
    ],
  },
});

3. Install your language's toolchain — CrossNative calls the real compiler, so it must be on your PATH. Install only what you need, then check with npx cross-native doctor:

  • Rustrustup + rustup target add wasm32-unknown-unknown
  • Go — Go 1.24+
  • Zig / C / C++ — one zig binary on your PATH (it compiles all three)

4. Write a function and call it — here in Rust, native/compute.rs:

use crossnative::crossnative;

#[crossnative]
pub fn heavy(iterations: u32) -> f64 {
    let mut sum = 0.0;
    for i in 0..iterations {
        sum += (i as f64).sqrt();
    }
    sum
}
import {createNativeModule} from 'react-native-cross-native';
import WASM from './native/compute.rs'; // Metro compiles this for you

const compute = await createNativeModule({
  name: 'compute',
  source: 'compute.rs',
  language: 'rust',
  bytes: WASM,
});

const result = await compute.call('heavy', [1_000_000]);

Important: This already runs on a background thread, on a portable interpreter. For heavy number-crunching, follow your platform below to get native speed.

Getting started on iOS

iOS does not allow generating machine code at runtime, so for native speed you compile your module into the app as a static library and call it directly. After the shared setup above:

1. Build the static library. For Zig, C, C++, or Go:

npx cross-native build-native native \
  --language zig --entry compute.zig \
  --symbol _zig --target aarch64-ios-simulator \
  --out native/ios/libcompute_zig.a

Use --target aarch64-ios-simulator for the simulator and aarch64-ios for a device. Each language has its own entry-symbol suffix: Rust '' (empty), Zig _zig, C _c, C++ _cpp, Go _go. For Rust, build a staticlib crate instead:

cargo build --target aarch64-apple-ios-sim --release   # or aarch64-apple-ios

2. Force-load the library in your ios/Podfile post_install block, so its entry symbols survive linking:

flag = '-force_load $(PODS_ROOT)/../native/ios/libcompute_zig.a'
# add `flag` to OTHER_LDFLAGS on your app target

Run cd ios && pod install again after editing the Podfile.

3. Tell createNativeModule to use the linked code — pass linked: true and the matching symbol suffix instead of bytes:

const compute = await createNativeModule({
  name: 'compute',
  source: 'compute.zig',
  language: 'zig',
  linked: true,
  linkedSymbol: '_zig', // Rust uses '' (empty)
});

Rebuild the app (npx react-native run-ios). The call now runs as native code.

Getting started on Android

Android can load compiled machine code at runtime, so for native speed you compile your module ahead-of-time (AOT) with wamrc. After the shared setup above:

1. Get wamrc — the ahead-of-time compiler from the WebAssembly Micro Runtime. It's a large LLVM-based tool, so it isn't bundled; build it once:

git clone https://github.com/bytecodealliance/wasm-micro-runtime
cd wasm-micro-runtime/wamr-compiler
./build_llvm.sh          # one-time, slow
mkdir build && cd build && cmake .. && make
# the binary is now ./wamrc

2. Start Metro with AOT enabled — point CrossNative at wamrc and turn AOT on:

CROSSNATIVE_AOT=1 CROSSNATIVE_WAMRC=/absolute/path/to/wamrc npm start

The transformer now compiles each module to native .aot code. You'll see aot .../compute.aot lines in the Metro output. Rebuild the app (npx react-native run-android) and every language runs at native speed — typically tens of milliseconds where the interpreter took seconds.

Architecture note. An .aot file is native code for one CPU architecture. CrossNative builds it for arm64 (aarch64), which covers real devices and Apple-silicon emulators; on an x86_64 device or emulator the runtime falls back to the interpreter for that module. If CROSSNATIVE_WAMRC is unset, CrossNative logs a note and ships the interpreter .wasm rather than failing the build.

Passing arrays

Numbers and booleans cross by value. Arrays are copied into the module's memory with the buffer helpers:

import {inBuffer, outBuffer} from 'react-native-cross-native';

await compute.call('sum', [inBuffer([1, 2, 3]), 3]);        // module reads
await compute.call('scale', [outBuffer(9), 9]);             // module writes

Packages

CrossNative is a small monorepo. Installing react-native-cross-native brings in the rest:

| Package | Role | | --- | --- | | react-native-cross-native | The React Native package: JSI native module, WASM runtime, and the Metro transformer | | @cross-native/compiler | The cross-native CLI and the per-language compile drivers | | @cross-native/core | The runtime and JS API (createNativeModule, buffers, plugins) | | @cross-native/languages | The language registry and validation |

Documentation

The full guide — every language, argument marshalling, and troubleshooting — lives in docs/getting-started.md.

License

Apache-2.0