Micro Frontends (MFEs) are an architectural approach where a frontend application is broken down into smaller, independent parts that can be developed, deployed, and maintained separately. Communication between these MFEs is crucial to ensure seamless functionality and user experience. Below are common strategies for enabling communication between MFEs in a React-based application, along with examples:
1. Custom Events (Event Bus)
MFEs can communicate by emitting and listening to custom browser events. This is a loosely coupled approach, allowing MFEs to interact without direct dependencies.
How it works:
- One MFE dispatches a custom event with data.
- Other MFEs listen for this event and react to the data.
Example:
// MFE 1: Emitting an event
const sendMessage = (message) => {
const event = new CustomEvent('mfeMessage', { detail: { message } });
window.dispatchEvent(event);
};
// Button in MFE 1
<button onClick={() => sendMessage('Hello from MFE 1')}>
Send Message
</button>
// MFE 2: Listening for the event
useEffect(() => {
const handleMessage = (event) => {
console.log('Received in MFE 2:', event.detail.message);
// Update state or UI based on event.detail.message
};
window.addEventListener('mfeMessage', handleMessage);
return () => {
window.removeEventListener('mfeMessage', handleMessage);
};
}, []);
Pros:
- Decoupled communication.
- Works across different frameworks (not React-specific).
- Simple to implement for basic use cases.
Cons:
- Event names can collide if not namespaced properly.
- Debugging can be challenging with many events.
- No strong typing or contract enforcement.
2. Shared State Management (e.g., Redux, Zustand)
A centralized state management library can be shared across MFEs to store and manage shared state.
How it works:
- A shared state library is exposed globally (e.g., via a window object or a shared module).
- Each MFE can read from or dispatch actions to update the shared state.
Example with Zustand:
// Shared state module (shared-store.js)
import create from 'zustand';
export const useSharedStore = create((set) => ({
sharedData: '',
setSharedData: (data) => set({ sharedData: data }),
}));
// MFE 1: Update shared state
import { useSharedStore } from './shared-store';
const MFE1Component = () => {
const { setSharedData } = useSharedStore();
return (
<button onClick={() => setSharedData('Data from MFE 1')}>
Update Shared State
</button>
);
};
// MFE 2: Read shared state
import { useSharedStore } from './shared-store';
const MFE2Component = () => {
const { sharedData } = useSharedStore();
return <div>Received: {sharedData}</div>;
};
Setup:
- The shared store can be bundled as a separate module and imported by each MFE.
- Alternatively, expose it via
window.sharedStore for MFEs to access.
Pros:
- Structured and predictable state management.
- Easy to scale for complex applications.
- Type-safe with TypeScript.
Cons:
- Requires additional setup for sharing the store.
- Tight coupling if the store schema is shared across MFEs.
- Overhead of maintaining a state management library.
3. Props Passing via a Host Application
A host or shell application can orchestrate communication by passing props to MFEs, treating them as components.
How it works:
- The host application renders MFEs and passes callbacks or data as props.
- MFEs communicate by invoking these callbacks, which the host handles.
Example:
// Host App
import MFE1 from 'mfe1/RemoteComponent';
import MFE2 from 'mfe2/RemoteComponent';
const HostApp = () => {
const [sharedData, setSharedData] = useState('');
return (
<div>
<MFE1 onDataChange={setSharedData} />
<MFE2 sharedData={sharedData} />
</div>
);
};
// MFE 1: Send data via callback
const MFE1Component = ({ onDataChange }) => {
return (
<button onClick={() => onDataChange('Data from MFE 1')}>
Send Data
</button>
);
};
// MFE 2: Receive data via props
const MFE2Component = ({ sharedData }) => {
return <div>Received: {sharedData}</div>;
};
Setup:
- Use a module federation tool like Webpack Module Federation to load MFEs dynamically.
- The host app exposes a contract for props that MFEs must adhere to.
Pros:
- Simple and explicit communication.
- Leverages React’s component model.
- Easy to debug and test.
Cons:
- Tightly couples MFEs to the host’s interface.
- Less flexible for dynamic or runtime-loaded MFEs.
- Requires a clear contract for props.
4. URL-based Communication
MFEs can communicate by updating and reading the browser’s URL (e.g., query parameters or hash).
How it works:
- One MFE updates the URL with data (e.g., query params).
- Other MFEs listen for URL changes and extract the data.
Example:
// MFE 1: Update URL
import { useHistory } from 'react-router-dom';
const MFE1Component = () => {
const history = useHistory();
const sendData = () => {
history.push({
pathname: '/mfe1',
search: `?data=${encodeURIComponent('Hello from MFE 1')}`,
});
};
return <button onClick={sendData}>Send Data via URL</button>;
};
// MFE 2: Read URL
import { useLocation } from 'react-router-dom';
const MFE2Component = () => {
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const data = queryParams.get('data');
return <div>Received: {data || 'No data'}</div>;
};
Pros:
- No direct dependency between MFEs.
- Persists state in the URL, enabling deep linking.
- Works well for navigation-driven communication.
Cons:
- Limited to small amounts of data (URL length restrictions).
- Requires careful encoding/decoding of data.
- Can clutter the URL if overused.
5. Window.postMessage
This approach uses the browser’s postMessage API for cross-origin or cross-window communication, ideal for MFEs hosted in iframes or different domains.
How it works:
- One MFE sends a message to another MFE or the host using
window.postMessage.
- The receiver listens for messages and processes them.
Example:
// MFE 1: Send message
const sendMessage = () => {
window.postMessage({ type: 'MFE_MESSAGE', payload: 'Hello from MFE 1' }, '*');
};
return <button onClick={sendMessage}>Send Message</button>;
// MFE 2: Receive message
useEffect(() => {
const handleMessage = (event) => {
if (event.data.type === 'MFE_MESSAGE') {
console.log('Received in MFE 2:', event.data.payload);
// Update state or UI
}
};
window.addEventListener('message', handleMessage);
return () => {
window.removeEventListener('message', handleMessage);
};
}, []);
Pros:
- Works across different origins (e.g., iframes, different domains).
- Flexible for complex scenarios.
- Secure if origin checks are implemented.
Cons:
- Requires careful origin validation to prevent security issues.
- More complex than custom events for same-origin MFEs.
- Message handling can become messy if not structured.
6. Shared Storage (e.g., localStorage, sessionStorage)
MFEs can use browser storage mechanisms like localStorage or sessionStorage to share data.
How it works:
- One MFE writes data to
localStorage.
- Other MFEs listen for storage events or poll
localStorage for updates.
Example:
// MFE 1: Write to localStorage
const sendData = () => {
localStorage.setItem('mfeData', JSON.stringify({ message: 'Hello from MFE 1' }));
};
// MFE 2: Listen for storage changes
useEffect(() => {
const handleStorageChange = (event) => {
if (event.key === 'mfeData') {
const data = JSON.parse(event.newValue);
console.log('Received in MFE 2:', data.message);
}
};
window.addEventListener('storage', handleStorageChange);
return () => {
window.removeEventListener('storage', handleStorageChange);
};
}, []);
Pros:
- Simple to implement.
- Persists data across page reloads (
localStorage).
- Works across different origins if same storage is accessible.
Cons:
- Limited storage size (5-10 MB for
localStorage).
- Performance issues with frequent writes/reads.
- Requires manual serialization/deserialization.
7. Pub/Sub Libraries (e.g., PubSubJS)
Use a lightweight publish-subscribe library like PubSubJS to manage communication between MFEs.
How it works:
- MFEs publish messages to specific topics.
- Other MFEs subscribe to these topics to receive messages.
Example:
import PubSub from 'pubsub-js';
// MFE 1: Publish message
const sendMessage = () => {
PubSub.publish('MFE_MESSAGE', { message: 'Hello from MFE 1' });
};
// MFE 2: Subscribe to message
useEffect(() => {
const subscription = PubSub.subscribe('MFE_MESSAGE', (msg, data) => {
console.log('Received in MFE 2:', data.message);
});
return () => {
PubSub.unsubscribe(subscription);
};
}, []);
Pros:
- Structured and decoupled.
- Easy to manage multiple topics.
- Works across frameworks.
Cons:
- Adds a dependency to the project.
- Requires careful topic naming to avoid conflicts.
- Overhead of managing subscriptions.
Recommendations
- For simple communication: Use Custom Events or Props Passing for quick, lightweight solutions.
- For complex state management: Use Shared State Management (e.g., Zustand, Redux) for scalability and structure.
- For cross-origin scenarios: Use Window.postMessage with proper origin validation.
- For navigation-driven apps: Use URL-based Communication to leverage browser history.
- For decoupled systems: Consider Pub/Sub Libraries or Shared Storage for flexibility.
Best Practices
- Namespace events/topics: Prevent conflicts by using unique prefixes (e.g.,
mfe1.eventName).
- Define contracts: Clearly document the data structure for communication to avoid errors.
- Handle errors gracefully: Add error boundaries and validation for incoming data.
- Use TypeScript: Enforce types for shared data to improve maintainability.
- Avoid tight coupling: Prefer loosely coupled methods like events or Pub/Sub over direct prop passing when possible.
Below, I’ll provide detailed step-by-step guides for implementing each of the seven communication methods for Micro Frontends (MFEs) in a React-based application. Each method will include setup instructions, code examples, and considerations for using Webpack Module Federation (a common tool for MFEs). The examples assume you’re using React with Webpack Module Federation for MFE integration, but the communication patterns are adaptable to other setups.
How it works
Prerequisites
- Node.js and npm/yarn installed.
- Two or more React MFEs and a host/shell application.
- Webpack Module Federation configured for loading MFEs.
- Basic knowledge of React, Webpack, and JavaScript/TypeScript.
Webpack Module Federation Setup (Common for All Methods)
Before diving into communication methods, ensure your MFEs are set up with Webpack Module Federation. Here’s a basic setup for a host and two MFEs:
- Host Application (
host-app)
Directory: host-app
Install dependencies:
npm init -y
npm install react react-dom webpack webpack-cli webpack-dev-server html-webpack-plugin @module-federation/nextjs-mf --save-dev
Webpack config (host-app/webpack.config.js):
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
publicPath: 'http://localhost:3000/',
},
devServer: {
port: 3000,
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: { presets: ['@babel/preset-react'] },
},
],
},
plugins: [
new HtmlWebpackPlugin({ template: './public/index.html' }),
new ModuleFederationPlugin({
name: 'host',
remotes: {
mfe1: 'mfe1@http://localhost:3001/remoteEntry.js',
mfe2: 'mfe2@http://localhost:3002/remoteEntry.js',
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
}),
],
};
Entry point (host-app/src/index.js):
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
Host app (host-app/src/App.jsx):
import React from 'react';
import MFE1 from 'mfe1/RemoteComponent';
import MFE2 from 'mfe2/RemoteComponent';
const App = () => (
<div>
<h1>Host Application</h1>
<MFE1 />
<MFE2 />
</div>
);
export default App;
- MFE 1 (
mfe1)
Directory: mfe1
Install dependencies (same as host).
Webpack config (mfe1/webpack.config.js):
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
publicPath: 'http://localhost:3001/',
},
devServer: {
port: 3001,
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: { presets: ['@babel/preset-react'] },
},
],
},
plugins: [
new HtmlWebpackPlugin({ template: './public/index.html' }),
new ModuleFederationPlugin({
name: 'mfe1',
filename: 'remoteEntry.js',
exposes: {
'./RemoteComponent': './src/RemoteComponent',
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
}),
],
};
Remote component (mfe1/src/RemoteComponent.jsx):
import React from 'react';
const RemoteComponent = () => <div>MFE 1 Component</div>;
export default RemoteComponent;
MFE 2 (mfe2)
Similar setup to MFE 1, but use port 3002 and expose a different component.
Run the applications:
# In host-app directory
npm start
# In mfe1 directory
npm start
# In mfe2 directory
npm start
- Host runs on
http://localhost:3000, MFE1 on http://localhost:3001, MFE2 on http://localhost:3002.
Now, let’s implement each communication method.
1. Custom Events (Event Bus)
Steps
- Define an event name: Use a unique, namespaced event name (e.g.,
mfe1.message).
- Emit event in MFE 1: Dispatch a custom event with data using
window.dispatchEvent.
- Listen for event in MFE 2: Add an event listener in MFE 2 to handle the event.
- Clean up: Remove event listeners on component unmount to prevent memory leaks.
- Test: Verify the event is received and data is processed.
Example
- MFE 1 (
mfe1/src/RemoteComponent.jsx):
import React from 'react';
const RemoteComponent = () => {
const sendMessage = (message) => {
const event = new CustomEvent('mfe1.message', { detail: { message } });
window.dispatchEvent(event);
};
return (
<div>
<h2>MFE 1</h2>
<button onClick={() => sendMessage('Hello from MFE 1')}>
Send Message
</button>
</div>
);
};
export default RemoteComponent;
- MFE 2 (
mfe2/src/RemoteComponent.jsx):
import React, { useEffect, useState } from 'react';
const RemoteComponent = () => {
const [message, setMessage] = useState('');
useEffect(() => {
const handleMessage = (event) => {
setMessage(event.detail.message);
};
window.addEventListener('mfe1.message', handleMessage);
return () => {
window.removeEventListener('mfe1.message', handleMessage);
};
}, []);
return (
<div>
<h2>MFE 2</h2>
<p>Received: {message || 'No message'}</p>
</div>
);
};
export default RemoteComponent;
- Host App (
host-app/src/App.jsx):
- No changes needed; just render MFE1 and MFE2 as shown in the setup.
Considerations
- Namespace events (e.g.,
mfe1.message) to avoid conflicts.
- Use TypeScript for type-safe event data.
- Avoid overuse, as debugging many events can be complex.
2. Shared State Management (Zustand)
Steps
- Create a shared store: Define a Zustand store in a shared module.
- Expose the store: Bundle the store as a shared module or attach it to
window.
- Configure Module Federation: Expose the store from a shared module or one MFE.
- Use in MFEs: Import and use the store in MFE1 and MFE2.
- Test: Verify state updates propagate across MFEs.
Example
- Shared Store (
shared-store/index.js):
import create from 'zustand';
export const useSharedStore = create((set) => ({
sharedData: '',
setSharedData: (data) => set({ sharedData: data }),
}));
MFE 1 Webpack Config (mfe1/webpack.config.js):
Add the shared store as an exposed module:
plugins: [
new ModuleFederationPlugin({
name: 'mfe1',
filename: 'remoteEntry.js',
exposes: {
'./RemoteComponent': './src/RemoteComponent',
'./SharedStore': '../shared-store/index.js', // Expose shared store
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true }, zustand: { singleton: true } },
}),
],
MFE 1 (mfe1/src/RemoteComponent.jsx):
import React from 'react';
import { useSharedStore } from 'mfe1/SharedStore';
const RemoteComponent = () => {
const { setSharedData } = useSharedStore();
return (
<div>
<h2>MFE 1</h2>
<button onClick={() => setSharedData('Data from MFE 1')}>
Update Shared State
</button>
</div>
);
};
export default RemoteComponent;
- MFE 2 (
mfe2/src/RemoteComponent.jsx):
import React from 'react';
import { useSharedStore } from 'mfe1/SharedStore';
const RemoteComponent = () => {
const { sharedData } = useSharedStore();
return (
<div>
<h2>MFE 2</h2>
<p>Received: {sharedData || 'No data'}</p>
</div>
);
};
export default RemoteComponent;
Host App Webpack Config:
Update to include the shared store:
remotes: {
mfe1: 'mfe1@http://localhost:3001/remoteEntry.js',
mfe2: 'mfe2@http://localhost:3002/remoteEntry.js',
},
shared: { react: { singleton: true }, 'react-dom': { singleton: true }, zustand: { singleton: true } },
Considerations
- Use a singleton for the store to avoid multiple instances.
- Ensure all MFEs share the same version of Zustand.
- Consider TypeScript for type-safe state management.
3. Props Passing via Host Application
Steps
- Define props in Host: Create state and callbacks in the host app.
- Pass props to MFEs: Pass data and callbacks to MFE components.
- Handle props in MFEs: Use the passed props to send/receive data.
- Configure Module Federation: Ensure MFEs are loaded as components.
- Test: Verify props are passed and callbacks work.
Example
- Host App (
host-app/src/App.jsx):
import React, { useState } from 'react';
import MFE1 from 'mfe1/RemoteComponent';
import MFE2 from 'mfe2/RemoteComponent';
const App = () => {
const [sharedData, setSharedData] = useState('');
return (
<div>
<h1>Host Application</h1>
<MFE1 onDataChange={setSharedData} />
<MFE2 sharedData={sharedData} />
</div>
);
};
export default App;
- MFE 1 (
mfe1/src/RemoteComponent.jsx):
import React from 'react';
const RemoteComponent = ({ onDataChange }) => {
return (
<div>
<h2>MFE 1</h2>
<button onClick={() => onDataChange('Data from MFE 1')}>
Send Data
</button>
</div>
);
};
export default RemoteComponent;
- MFE 2 (
mfe2/src/RemoteComponent.jsx):
import React from 'react';
const RemoteComponent = ({ sharedData }) => {
return (
<div>
<h2>MFE 2</h2>
<p>Received: {sharedData || 'No data'}</p>
</div>
);
};
export default RemoteComponent;
Considerations
- Define a clear prop contract to ensure compatibility.
- Avoid passing complex objects to minimize serialization issues.
- Suitable for tightly integrated MFEs but less flexible for dynamic loading.
4. URL-based Communication
Steps
- Install React Router: Add
react-router-dom to all MFEs and the host.
- Update URL in MFE 1: Use
useHistory to update query parameters.
- Read URL in MFE 2: Use
useLocation to read query parameters.
- Synchronize routing: Ensure the host and MFEs share the same routing context.
- Test: Verify URL updates and data extraction.
Example
npm install react-router-dom
- Host App (
host-app/src/App.jsx):
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import MFE1 from 'mfe1/RemoteComponent';
import MFE2 from 'mfe2/RemoteComponent';
const App = () => (
<BrowserRouter>
<h1>Host Application</h1>
<MFE1 />
<MFE2 />
</BrowserRouter>
);
export default App;
- MFE 1 (
mfe1/src/RemoteComponent.jsx):
import React from 'react';
import { useHistory } from 'react-router-dom';
const RemoteComponent = () => {
const history = useHistory();
const sendData = () => {
history.push({
pathname: '/mfe1',
search: `?data=${encodeURIComponent('Hello from MFE 1')}`,
});
};
return (
<div>
<h2>MFE 1</h2>
<button onClick={sendData}>Send Data via URL</button>
</div>
);
};
export default RemoteComponent;
- MFE 2 (
mfe2/src/RemoteComponent.jsx):
import React from 'react';
import { useLocation } from 'react-router-dom';
const RemoteComponent = () => {
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const data = queryParams.get('data');
return (
<div>
<h2>MFE 2</h2>
<p>Received: {data || 'No data'}</p>
</div>
);
};
export default RemoteComponent;
Webpack Config (Shared Dependencies):
Add react-router-dom to shared dependencies in all Webpack configs:
shared: { react: { singleton: true }, 'react-dom': { singleton: true }, 'react-router-dom': { singleton: true } },
Considerations
- Use a shared routing context to avoid conflicts.
- Encode data to prevent URL injection issues.
- Limit data size due to URL length restrictions.
5. Window.postMessage
Steps
- Send message in MFE 1: Use
window.postMessage to send data.
- Listen for message in MFE 2: Add a
message event listener.
- Validate origin: Check the message’s origin for security.
- Clean up: Remove event listeners on unmount.
- Test: Verify messages are sent and received.
Example
- MFE 1 (
mfe1/src/RemoteComponent.jsx):
import React from 'react';
const RemoteComponent = () => {
const sendMessage = () => {
window.postMessage(
{ type: 'MFE_MESSAGE', payload: 'Hello from MFE 1' },
'http://localhost:3000' // Host origin
);
};
return (
<div>
<h2>MFE 1</h2>
<button onClick={sendMessage}>Send Message</button>
</div>
);
};
export default RemoteComponent;
- MFE 2 (
mfe2/src/RemoteComponent.jsx):
import React, { useEffect, useState } from 'react';
const RemoteComponent = () => {
const [message, setMessage] = useState('');
useEffect(() => {
const handleMessage = (event) => {
if (event.origin !== 'http://localhost:3000') return; // Validate origin
if (event.data.type === 'MFE_MESSAGE') {
setMessage(event.data.payload);
}
};
window.addEventListener('message', handleMessage);
return () => {
window.removeEventListener('message', handleMessage);
};
}, []);
return (
<div>
<h2>MFE 2</h2>
<p>Received: {message || 'No message'}</p>
</div>
);
};
export default RemoteComponent;
Considerations
- Always validate the
event.origin to prevent security risks.
- Use a clear message format (e.g.,
{ type, payload }).
- Suitable for cross-origin or iframe-based MFEs.
6. Shared Storage (localStorage)
Steps
- Write to storage in MFE 1: Use
localStorage.setItem to store data.
- Listen for storage events in MFE 2: Add a
storage event listener.
- Read storage directly (optional): Poll
localStorage if needed.
- Clean up: Remove event listeners on unmount.
- Test: Verify data is written and read correctly.
Example
- MFE 1 (
mfe1/src/RemoteComponent.jsx):
import React from 'react';
const RemoteComponent = () => {
const sendData = () => {
localStorage.setItem('mfeData', JSON.stringify({ message: 'Hello from MFE 1' }));
};
return (
<div>
<h2>MFE 1</h2>
<button onClick={sendData}>Send Data</button>
</div>
);
};
export default RemoteComponent;
- MFE 2 (
mfe2/src/RemoteComponent.jsx):
import React, { useEffect, useState } from 'react';
const RemoteComponent = () => {
const [message, setMessage] = useState('');
useEffect(() => {
const handleStorageChange = (event) => {
if (event.key === 'mfeData') {
const data = JSON.parse(event.newValue);
setMessage(data.message);
}
};
window.addEventListener('storage', handleStorageChange);
// Initial read
const storedData = localStorage.getItem('mfeData');
if (storedData) {
setMessage(JSON.parse(storedData).message);
}
return () => {
window.removeEventListener('storage', handleStorageChange);
};
}, []);
return (
<div>
<h2>MFE 2</h2>
<p>Received: {message || 'No data'}</p>
</div>
);
};
export default RemoteComponent;
Considerations
- Use
sessionStorage for session-specific data.
- Serialize/deserialize data carefully.
- Be aware of storage size limits (5-10 MB).
7. Pub/Sub Libraries (PubSubJS)
Steps
- Install PubSubJS: Add
pubsub-js to all MFEs.
- Publish in MFE 1: Use
PubSub.publish to send messages.
- Subscribe in MFE 2: Use
PubSub.subscribe to receive messages.
- Share PubSubJS: Configure Module Federation to share the library.
- Test: Verify publish/subscribe works across MFEs.
Example
npm install pubsub-js
- MFE 1 (
mfe1/src/RemoteComponent.jsx):
import React from 'react';
import PubSub from 'pubsub-js';
const RemoteComponent = () => {
const sendMessage = () => {
PubSub.publish('MFE_MESSAGE', { message: 'Hello from MFE 1' });
};
return (
<div>
<h2>MFE 1</h2>
<button onClick={sendMessage}>Send Message</button>
</div>
);
};
export default RemoteComponent;
- MFE 2 (
mfe2/src/RemoteComponent.jsx):
import React, { useEffect, useState } from 'react';
import PubSub from 'pubsub-js';
const RemoteComponent = () => {
const [message, setMessage] = useState('');
useEffect(() => {
const subscription = PubSub.subscribe('MFE_MESSAGE', (msg, data) => {
setMessage(data.message);
});
return () => {
PubSub.unsubscribe(subscription);
};
}, []);
return (
<div>
<h2>MFE 2</h2>
<p>Received: {message || 'No message'}</p>
</div>
);
};
export default RemoteComponent;
- Webpack Config (Shared Dependencies):
- Add
pubsub-js to shared dependencies:
javascript shared: { react: { singleton: true }, 'react-dom': { singleton: true }, 'pubsub-js': { singleton: true } },
Considerations
- Use unique topic names to avoid conflicts.
- Manage subscriptions to prevent memory leaks.
- Consider TypeScript for typed messages.
Testing and Verification
For each method:
- Run all apps: Start the host (
npm start on port 3000), MFE1 (port 3001), and MFE2 (port 3002).
- Interact with MFE 1: Trigger the action (e.g., click a button) to send data.
- Verify in MFE 2: Check that MFE 2 displays or logs the received data.
- Debug: Use browser DevTools to inspect events, storage, or network calls, Add console logs to trace data flow.
Best Practices (Across All Methods)
- Namespace events/topics: Use prefixes like
mfe1. to avoid collisions.
- Type safety: Use TypeScript to define data contracts.
- Error handling: Add try-catch blocks and validation for incoming data.
- Clean up: Remove event listeners/subscriptions on unmount.
- Module Federation: Share dependencies like React, Zustand, or PubSubJS as singletons to avoid version conflicts.
- Documentation: Define clear communication contracts for each MFE.
If you need help with a specific method, troubleshooting, or integrating with a different setup (e.g., single-spa, iframe-based MFEs), let me know!