Metro (React Native)
Deploy your React Native applications with Module Federation to Zephyr Cloud using Metro bundler. The Zephyr Metro plugin integrates seamlessly with Metro's build process and enables micro-frontend architectures for React Native applications.
Quick Setup with Codemod
This detects your bundler and configures Zephyr automatically. Learn more →
For manual setup, continue below.
Installation
Install the Metro plugin and required Module Federation dependencies in your project:
npm add --dev zephyr-metro-plugin @module-federation/metro @module-federation/runtime
yarn add --dev zephyr-metro-plugin @module-federation/metro @module-federation/runtime
pnpm add --dev zephyr-metro-plugin @module-federation/metro @module-federation/runtime
bun add --dev zephyr-metro-plugin @module-federation/metro @module-federation/runtime
deno add --dev npm:zephyr-metro-plugin npm:@module-federation/metro npm:@module-federation/runtime
If you use React Native CLI custom commands, install the React Native CLI command package:
npm add --dev @module-federation/metro-plugin-rnc-cli
yarn add --dev @module-federation/metro-plugin-rnc-cli
pnpm add --dev @module-federation/metro-plugin-rnc-cli
bun add --dev @module-federation/metro-plugin-rnc-cli
deno add --dev npm:@module-federation/metro-plugin-rnc-cli
If you use RNEF, install the RNEF Module Federation plugin:
npm add --dev @module-federation/metro-plugin-rnef
yarn add --dev @module-federation/metro-plugin-rnef
pnpm add --dev @module-federation/metro-plugin-rnef
bun add --dev @module-federation/metro-plugin-rnef
deno add --dev npm:@module-federation/metro-plugin-rnef
To enable React Native OTA updates in a host app, also install the native cache runtime:
npm add zephyr-native-cache
yarn add zephyr-native-cache
pnpm add zephyr-native-cache
bun add zephyr-native-cache
deno add npm:zephyr-native-cache
Module Federation with Metro
Metro bundler supports Module Federation through the @module-federation/metro package, allowing you to create host and mini applications (remotes) in React Native.
Mini Application (Remote)
Mini applications expose modules to be consumed by host applications:
// metro.config.js
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const { withModuleFederation } = require('@module-federation/metro');
const { withZephyr } = require('zephyr-metro-plugin');
const config = {
resolver: { useWatchman: false },
};
const mfConfig = {
name: 'miniApp',
filename: 'miniApp.bundle',
exposes: {
'./example': './src/example.tsx',
},
shared: {
react: {
singleton: true,
eager: false,
requiredVersion: '19.1.0',
version: '19.1.0',
import: false,
},
'react-native': {
singleton: true,
eager: false,
requiredVersion: '0.80.0',
version: '0.80.0',
import: false,
},
},
shareStrategy: 'version-first',
};
async function getConfig() {
const baseConfig = mergeConfig(getDefaultConfig(__dirname), config);
const zephyrConfig = await withZephyr({
name: mfConfig.name,
target: process.env.ZEPHYR_TARGET === 'android' ? 'android' : 'ios',
})(baseConfig);
return withModuleFederation(zephyrConfig, mfConfig, {
flags: {
unstable_patchHMRClient: true,
unstable_patchInitializeCore: true,
unstable_patchRuntimeRequire: true,
},
});
}
module.exports = getConfig();
React Native CLI Configuration
Create or modify react-native.config.js to enable bundling with Zephyr:
// react-native.config.js
const commands = require('@module-federation/metro-plugin-rnc-cli');
const { updateManifest } = require('@module-federation/metro');
const { zephyrCommandWrapper } = require('zephyr-metro-plugin');
const wrappedFuncPromise = zephyrCommandWrapper(
commands.bundleMFRemoteCommand.func,
commands.loadMetroConfig,
() => {
updateManifest(
global.__METRO_FEDERATION_MANIFEST_PATH,
global.__METRO_FEDERATION_CONFIG,
);
},
);
const zephyrCommand = {
name: 'bundle-mf-remote',
description:
'Bundles a Module Federation remote, including its container entry and all exposed modules for consumption by host applications',
func: async (...args) => {
const wrappedFunc = await wrappedFuncPromise;
return wrappedFunc(...args);
},
options: commands.bundleMFRemoteCommand.options,
};
module.exports = {
commands: [zephyrCommand],
};
Bundle Mini Application
Bundle your mini application for different platforms:
# Bundle for iOS
npx react-native bundle-mf-remote --platform ios --dev false
# Bundle for Android
npx react-native bundle-mf-remote --platform android --dev false
Host Application (Consumer)
Host applications load and orchestrate mini-applications:
// metro.config.js
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const { withZephyr } = require('zephyr-metro-plugin');
const { withModuleFederation } = require('@module-federation/metro');
const miniAppPort = process.env.MINI_APP_PORT ?? '8082';
const useZephyrRemotes = process.env.ZEPHYR_REMOTE_RESOLUTION === '1';
const config = {
resolver: { useWatchman: false },
};
const mfConfig = {
name: 'hostApp',
remotes: {
miniApp: useZephyrRemotes
? 'zephyr:miniApp@yourEnvironment'
: `miniApp@http://localhost:${miniAppPort}/mf-manifest.json`,
},
shared: {
react: {
singleton: true,
eager: true,
requiredVersion: '19.1.0',
version: '19.1.0',
},
'react-native': {
singleton: true,
eager: true,
requiredVersion: '0.80.0',
version: '0.80.0',
},
},
shareStrategy: 'loaded-first',
runtimePlugins: [require.resolve('zephyr-native-cache/runtime-plugin')],
};
const getConfig = async () => {
const baseConfig = mergeConfig(getDefaultConfig(__dirname), config);
const zephyrConfig = await withZephyr({
name: mfConfig.name,
remotes: mfConfig.remotes,
target: process.env.ZEPHYR_TARGET === 'android' ? 'android' : 'ios',
})(baseConfig);
return withModuleFederation(zephyrConfig, mfConfig, {
flags: {
unstable_patchHMRClient: true,
unstable_patchInitializeCore: true,
unstable_patchRuntimeRequire: true,
},
});
};
module.exports = getConfig();
Use local HTTP manifest URLs during local development. Use Zephyr selectors such as zephyr:miniApp@yourEnvironment for builds that should resolve remotes from Zephyr Cloud.
To enable OTA behavior, register the native cache before the host loads remote bundles:
// index.js
import ZephyrNativeCache from 'zephyr-native-cache';
import { withAsyncStartup } from '@module-federation/metro/bootstrap';
import { AppRegistry } from 'react-native';
import { name as appName } from './app.json';
ZephyrNativeCache.register({
enablePolling: true,
pollIntervalMs: 5 * 60 * 1000,
});
AppRegistry.registerComponent(
appName,
withAsyncStartup(
() => require('./src/App'),
() => require('./src/Fallback'),
),
);
For full OTA behavior, update policies, cache status APIs, and rollback behavior, see React Native OTA Updates.
Zephyr Dependencies
Configure Zephyr dependencies in your host application's package.json:
{
"name": "hostApp",
"version": "1.0.0",
"zephyr:dependencies": {
"miniApp": "zephyr:miniApp@yourEnvironment"
}
}
For more details, see Remote Dependencies.
When building a host for Zephyr-resolved remotes, use the same selector in mfConfig.remotes and zephyr:dependencies so the Metro plugin can replace the selector with the resolved manifest URL.
Next Steps