Running Multiple Instances of React Native in Sandbox

Authors
Aliaksandr Babrykovich
Senior C++ Engineer
@
Callstack
No items found.

Module Federation, implemented in React Native through tools like Re.Pack, allows you to build large-scale "super apps" by loading different application features as independent JavaScript bundles. This micro-frontend architecture is powerful, enabling separate teams to develop and deploy their code without being tied to a monolithic release cycle. You can learn more about this approach in our talk on the subject here.

However, while this solves the code-splitting and delivery problem, it does not inherently provide runtime isolation. This becomes a critical security and stability concern when integrating code from multiple teams or third-party developers. Without isolation, there is a risk that one feature could access unauthorized data, conflict with another feature's dependencies, or crash the entire host application.

Introducing react-native-sandbox

To address these challenges, we created react-native-sandbox: a library that provides the components and native modules to run multiple, fully isolated React Native instances within a single application. It moves beyond simple code splitting to offer a sandboxed environment where each plugin's execution is completely contained.

This approach provides a powerful set of features and guarantees:

  • True Isolation: Each sandboxed instance runs in its own JavaScript runtime. This provides complete separation, preventing any interference between plugins, such as global namespace collisions or dependency conflicts, and the host application.
  • Configurable Security & Permissions: You have granular control over what each sandbox can do. By providing a "whitelist" of allowed native modules (allowedTurboModules), you can prevent third-party or experimental code from accessing sensitive APIs, thereby protecting user data and ensuring the host application's integrity.
  • Crash Containment: If a plugin suffers a critical error, the crash is contained entirely within its sandbox. The main application remains stable and responsive, preserving the overall user experience.
  • Secure Host-Sandbox Communication: While isolated, sandboxes can communicate with the host application through a controlled, message-passing API that enforces clear boundaries and prevents unsafe interactions.

To get started, download the project from npm and continue reading to learn more about the API:

npm install @callstack/react-native-sandbox

Putting it into practice

At the API level, you primarily interact with the SandboxReactNativeView component in your JSX. It's designed to feel natural within a standard React Native workflow.

<SandboxReactNativeView
  style={styles.sandboxContainer}
  jsBundleSource={'my-plugin'}
  moduleName={'PluginComponent'}
  onMessage={(message) => {
    console.log(`Message: ${message} received through safe API`)
  }}
  onError={(error) => {
    console.warn(`Sandbox error: ${error.message} - handle gracefully without crashing host`)
    return false// Error handled
  }}
/>

The jsBundleSource property is flexible and supports multiple ways to load your sandbox code:

  • Bundle name: Named bundle within the application
  • Local files: Bundled into the app during build time
  • Remote URL: https://example.com/plugin.bundle.js - dynamically loaded bundles

You can also control security and permissions using allowedTurboModules to specify which native modules the sandbox can access i.e. whitelist approach

Communicating with the Host

Communication between the isolated sandbox and the host application is handled through a secure message-passing API. This system is intentionally designed to be similar to the standard web postMessage API used with Web Workers, making it intuitive for developers.

The sandbox is provided with two globally available functions:

  • postMessage(message): Sends a serializable object from the sandbox to the host application.
  • setOnMessage(handler): Registers a callback function to handle messages received from the host.

Here’s how a component inside the sandbox can use this API to interact with its host:

function SandboxApp() {
  const [data, setData] = useState<string | undefined>();

  // 1. Listen for messages from the host
  const onMessage = useCallback((payload: unknown) => {
    setData(JSON.stringify(payload));
  }, []);

  useEffect(() => {
    globalThis.setOnMessage(onMessage);
  }, [onMessage]);

  // 2. Send a message back to the host
  const postMessageToHost = () => {
    globalThis.postMessage({ data: 'Hello from the Sandbox!' });
  };

  return (
    <View>
      <Button onPress={postMessageToHost} title="Send Data to Host" />
      <Text>Received from Host: {data}</Text>
    </View>
  );
}

Under the hood

Communication works between the host app and sandbox instances using Fabric Native Components.

It's show time

Our demo app is delightfully destructive, in the name of science, of course.

On the left, you'll see the "Main App" column running a component directly in the host application. On the right, the "Sandboxed" column runs the exact same component inside a SandboxReactNativeView. While they visually look identical, the sandboxed version is running in complete isolation.

function Demo() {
  return (
    <View>
      <View>
        <Text>Main App</Text>
        <CrashIfYouCanDemo />
      </View>
      <View>
        <Text>Sandboxed</Text>
        <SandboxReactNativeView
          style={styles.sandboxView}
          jsBundleSource="sandbox"
          moduleName="CrashIfYouCanDemo"
          onError={error => {
            Toast.show({...})
          }}
       />
    </View>
  )
}

CrashIfYouCanDemo component provides collection of buttons that would make any QA engineer reach for their coffee:

  • Crash App: Calls undefined global methods that would normally bring down the entire application
  • Overwrite Global: Hijacks console.log and other global variables, potentially breaking logging throughout the app
  • Access Blocked TurboModule: Attempts to use native modules that have been restricted for the sandbox
  • Infinite Loop: Because sometimes you just want to watch the world burn (or at least the JavaScript thread)

The magic happens when you start pressing buttons. Actions triggered in the main app can crash, freeze, or destabilize your entire application, exactly what you'd expect. But when you trigger the same destructive actions in the sandboxed environment? The sandbox contains the chaos, logs the errors gracefully, and your main app keeps running like nothing happened.

FAQ

Q: Is this safe for production? A: Yes, with proper configuration and testing. Each instance is isolated, and you control which native modules are exposed (more information in Security Considerations section of README)

Q: What about performance? A: Each sandboxed instance has its own JS runtime, so there is some overhead. For most plugin/multi-tenant use cases, the tradeoff is worth it for the isolation and flexibility.

Q: Can sandboxes communicate with each other? A: Not yet. All communication goes through the host app via a safe, message-passing API. But we consider it as possible enchantnment for future

Q: What platforms are supported? A: iOS at the moment

Q: Can I restrict what a plugin can do? A: Yes. You can whitelist which native modules (TurboModules) are available to each sandbox, limiting access to sensitive APIs.

What is next?

We believe react-native-sandbox will be really useful for developers building super apps, especially those integrating features from third-party developers where security and stability are paramount.

To make this even more seamless, we're actively working on first-class Re.Pack support in a follow-up version. This will handle much of the bundle management behind the scenes, making it incredibly simple to integrate and load sandboxed mini-apps.

Make sure to check out the project on Github and give it a star!

Table of contents
Planning a super app with React Native?

We help architect and develop super apps using modular, scalable designs.

Let’s chat
Link copied to clipboard!
//
Insights

Learn more about

Super Apps

Here's everything we published recently on this topic.

Sort
//
Super Apps

We can help you move
it forward!

At Callstack, we work with companies big and small, pushing React Native everyday.

Super App Development

Turn your application into a secure platform with independent cross-platform mini apps, developed both internally and externally.

Module Federation Development

Use Module Federation to provide dynamic features within React Native mobile applications post-release.