JSPM

@callstack/react-native-sandbox

0.2.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 6
  • Score
    100M100P100Q37660F
  • License MIT

A component to run multiple React Native instances side-by-side.

Package Exports

  • @callstack/react-native-sandbox
  • @callstack/react-native-sandbox/lib/commonjs/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@callstack/react-native-sandbox) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

@callstack/react-native-sandbox

npm version license

Library Documentation - For project overview, examples, and security considerations, see the main repository README.

This is the developer documentation for installing and using @callstack/react-native-sandbox in your React Native application.

📦 Installation

npm/yarn

npm install @callstack/react-native-sandbox
# or
yarn add @callstack/react-native-sandbox

iOS Setup

The package uses autolinking and supports the React Native New Architecture - no manual configuration required.

Android Setup

🚧 Under Construction - Android support is currently in development. Stay tuned for updates!

🎯 Basic Usage

For complete examples with both host and sandbox code, see the project examples.

import SandboxReactNativeView from '@callstack/react-native-sandbox';

<SandboxReactNativeView
  moduleName="YourSandboxModule"
  jsBundleSource="sandbox" // bundle file name
  onMessage={(data) => console.log('From sandbox:', data)}
  onError={(error) => console.error('Sandbox error:', error)}
/>

📚 API Reference

Component Props

Prop Type Required Default Description
moduleName string ☑️ - Name of the registered component to load from bundle specified in jsBundleSource
jsBundleSource string ☑️ - Name on file storage or URL to the JavaScript bundle to load
initialProperties object {} Initial props for the sandboxed app
launchOptions object {} Launch configuration options
allowedTurboModules string[] check here Additional TurboModules to allow
onMessage function undefined Callback for messages from sandbox
onError function undefined Callback for sandbox errors
style ViewStyle undefined Container styling

Ref Methods

interface SandboxReactNativeViewRef {
  postMessage: (message: unknown) => void;
}

Error Event Structure

interface ErrorEvent {
  name: string;        // Error type (e.g., 'TypeError')
  message: string;     // Error description
  stack?: string;      // Stack trace
  isFatal?: boolean;   // Whether error crashed the sandbox
}

🔒 Security & TurboModules

For detailed security considerations, see the Security section in the main README.

This package is built with React Native New Architecture using Fabric for optimal performance and type safety.

TurboModule Filtering

Use allowedTurboModules to control which native modules the sandbox can access:

<SandboxReactNativeView
  allowedTurboModules={['MyTrustedModule', 'AnotherSafeModule']}
  // ... other props
/>

Default allowed modules include essential React Native TurboModules like EventDispatcher, AppState, Networking, etc. See the source code for the complete list.

Note: This filtering works with both legacy native modules and new TurboModules, ensuring compatibility across React Native versions.

💬 Communication Patterns

Message Types

// Configuration updates
sandboxRef.current?.postMessage({
  type: 'config',
  payload: { theme: 'dark', locale: 'en' }
});

// Action commands  
sandboxRef.current?.postMessage({
  type: 'action',
  action: 'refresh'
});

// Data synchronization
sandboxRef.current?.postMessage({
  type: 'data',
  data: { users: [], posts: [] }
});

Message Validation

const handleMessage = (data: unknown) => {
  // Always validate messages from sandbox
  if (!data || typeof data !== 'object') return;
  
  const message = data as { type?: string; payload?: unknown };
  
  switch (message.type) {
    case 'ready':
      console.log('Sandbox is ready');
      break;
    case 'request':
      ref?.current.postMessage({requested: 'data'});
      break;
    default:
      console.warn('Unknown message type:', message.type);
  }
};

🎨 Advanced Usage

Dynamic Bundle Loading

const [bundleUrl, setBundleUrl] = useState<string>();

// Load bundle URL from your backend
useEffect(() => {
  fetch('/api/sandbox-config')
    .then(res => res.json())
    .then(config => setBundleUrl(config.bundleUrl));
}, []);

return (
  <SandboxReactNativeView
    moduleName="DynamicApp"
    jsBundleSource={bundleUrl}
    initialProperties={{ 
      userId: currentUser.id,
      theme: userPreferences.theme 
    }}
  />
);

Error Recovery Pattern

const [sandboxKey, setSandboxKey] = useState(0);

const handleError = (error: ErrorEvent) => {
  if (error.isFatal) {
    // Force re-mount to recover from fatal errors
    setSandboxKey(prev => prev + 1);
  }
};

return (
  <SandboxReactNativeView
    key={sandboxKey} // Re-mount on fatal errors
    onError={handleError}
    // ... other props
  />
);

Performance Monitoring

const handleMessage = (data: unknown) => {
  // Monitor sandbox performance metrics
  if (data?.type === 'performance') {
    console.log('Sandbox metrics:', data.metrics);
  }
};

⚡ Performance & Best Practices

Memory Management

  • Each sandbox creates a separate JavaScript context
  • Limit the number of concurrent sandbox instances
  • Use key prop to force re-mount when needed
  • Monitor memory usage in production

Bundle Optimization

// ✅ Good: Small, focused bundles
jsBundleSource="micro-app-dashboard.bundle.js"

// ❌ Avoid: Large monolithic bundles
jsBundleSource="entire-app-with-everything.bundle.js" 

Communication Efficiency

// ✅ Good: Batch updates
const batchedData = { users, posts, notifications };
sandboxRef.current?.postMessage({ type: 'batch_update', data: batchedData });

// ❌ Avoid: Frequent individual messages
users.forEach(user => sandboxRef.current?.postMessage({ type: 'user', user }));

🔧 Troubleshooting

Common Issues

1. Bundle Loading Fails

// ❌ Invalid bundle source
jsBundleSource="/invalid/path.js"

// ✅ Correct bundle source
jsBundleSource="https://cdn.example.com/app.bundle.js"
// or
jsBundleSource="micro-app.jsbundle"

2. TurboModule Access Denied

// ❌ Module not in whitelist
// Error: TurboModule 'MyModule' is not allowed

// ✅ Add to allowed list
allowedTurboModules={['MyModule']}

Debug Mode

Enable additional logging:

// In development
const isDebug = __DEV__;

<SandboxReactNativeView
  moduleName="DebugApp"
  launchOptions={{ debug: isDebug }}
  onError={(error) => {
    if (isDebug) {
      console.log('Full error details:', error);
    }
  }}
/>

🐛 Known Issues

iOS

  • Multiple rapid sandbox creation/destruction may cause memory spikes
  • Large bundles (>10MB) may impact initial load time
  • First sandbox load may be slower due to JavaScript context initialization

📄 More Information