Secure Communication Between Isolated React Native Instances
In our previous article, we introduced react-native-sandbox - a library that enables running multiple, isolated React Native instances within a single application. While the initial release focused on host-sandbox communication, we've now added a powerful new feature: direct communication between sandbox instances.
This enhancement opens up new possibilities for building complex multi-instance applications where sandboxes can communicate directly with each other, bypassing the host application entirely, while still maintaining the security.
Understanding Communication Patterns
Before diving into the implementation, let's clarify the two communication patterns available in react-native-sandbox
:
Sandbox-Host-Sandbox - The Old Approach
This was the only way sandboxes could communicate with each other - through the host application acting as a message dispatcher. This approach came with performance penalties and unnecessary boilerplate code.
This is the traditional communication pattern where sandboxes communicate through the host application:
Sandbox A → Host → Sandbox B
Implementation:
const handleMessage = (data: any) => {
// Boilerplate routing logic that grows with each sandbox
if (data.target === 'sandbox-b') {
sandboxBRef.current?.postMessage(data.payload);
} else if (data.target === 'sandbox-c') {
sandboxCRef.current?.postMessage(data.payload);
} else if (data.target === 'sandbox-d') {
sandboxDRef.current?.postMessage(data.payload);
}
// ... more boilerplate for each additional sandbox
};
<SandboxReactNativeView
ref={sandboxARef}
onMessage={handleMessage}
/>
<SandboxReactNativeView
ref={sandboxBRef}
onMessage={handleMessage}
/>
<SandboxReactNativeView
ref={sandboxCRef}
onMessage={handleMessage}
/>
// ... more sandboxes = more boilerplate
This approach required constant maintenance of routing logic in the host application and created tight coupling between sandboxes and the host's message handling implementation.
Sandbox-Sandbox - The New Approach
Sandboxes can now communicate directly with each other, eliminating the host-as-dispatcher pattern and all its associated problems.
This is the new direct communication pattern where sandboxes communicate directly:
Sandbox A ↔ Sandbox B
Implementation:
// Host application - NO message routing needed!export default
function App() {
return (
<View style={styles.flexRow}>
<View style={styles.flex10Margin}>
<SandboxReactNativeView
origin="A"
jsBundleSource="sandbox"
componentName="SandboxA"
allowedOrigins={['B']} // Simple configuration
/>
</View>
<View style={styles.flex10Margin}>
<SandboxReactNativeView
origin="B"
jsBundleSource="sandbox"
componentName="SandboxB"
allowedOrigins={['A']} // Simple configuration
/>
</View>
</View>
);
}
// Sandbox A - Direct communication
const sendToB = () => {
globalThis.postMessage({ type: 'increment', value: 1 }, 'B');
};
// Sandbox B - Direct communication
const sendToA = () => {
globalThis.postMessage({ type: 'decrement', value: 1 }, 'A');
};
With direct communication, sandboxes can send messages to each other without any involvement from the host. There’s no need for routing logic, no extra boilerplate, and no changes required in the host when adding new sandboxes. Just set the allowedOrigins
prop, and sandboxes can communicate directly and efficiently.
Security Model
Communication maintains the same security principles as the rest of react-native-sandbox:
Origin-Based Access Control
Each sandbox explicitly declares which other sandboxes can send messages to it through the allowedOrigins
prop:
// Sandbox A allows messages from Sandbox B
allowedOrigins={['B']}
// Sandbox B allows messages from Sandbox A
allowedOrigins={['A']}
Bidirectional Control
Communication is not automatically bidirectional. If Sandbox A wants to send messages to Sandbox B, both conditions must be met:
- Sandbox A's
allowedOrigins
includes 'B' (to send) - Sandbox B's
allowedOrigins
includes 'A' (to receive)
Dynamic Permissions
The allowedOrigins
can be updated at runtime, allowing for dynamic permission management:
const [allowCommunication, setAllowCommunication] = useState(false);
<SandboxReactNativeView
origin="A"
allowedOrigins={allowCommunication ? ['B'] : []}
// ... other props
/>
Demo time
Check out the p2p-counter
example, which demonstrates two sandboxes updating each other's state directly, no host routing required.
Conclusion
Direct communication between React Native sandbox instances represents a significant evolution in the react-native-sandbox library. It provides developers with the flexibility to build complex, multi-instance applications with direct communication capabilities while maintaining the security and isolation benefits that make sandboxing valuable.
Whether you're building a micro-frontend dashboard, or a plugin system, direct sandbox communication enables new architectural patterns that were previously difficult to achieve with React Native.
To get started with direct sandbox communication, check out the p2p-counter example and the complete API documentation.
Ready to build secure, scalable multi-instance applications?
Get started with react-native-sandbox
Learn more about
Security
Here's everything we published recently on this topic.
We can help you move
it forward!
At Callstack, we work with companies big and small, pushing React Native everyday.
Monitoring & Observability
Enable production-grade monitoring and observability for React Native apps with real-time insights and alerts.
Release Process Optimization
Ship faster with optimized CI/CD pipelines, automated deployments, and scalable release workflows for React Native apps.
