Hone logo
Hone
Problems

Building a React Micro-Frontend Architecture with Module Federation

This challenge focuses on implementing Module Federation in a React-based application to create a micro-frontend architecture. You will build two separate React applications: a host application and a remote application. The remote application will expose a React component that the host application will dynamically load and render. This is a fundamental step towards building scalable, independently deployable frontends.

Problem Description

Your task is to set up a basic Module Federation architecture using React and TypeScript. You need to create two distinct applications:

  1. Host Application: This application will be the main entry point. It will be responsible for consuming and rendering components exposed by other applications.
  2. Remote Application: This application will expose one or more components that can be dynamically loaded by the host application.

You will need to configure Webpack to enable Module Federation for both applications and ensure that the remote component is successfully rendered within the host.

Key Requirements:

  • Two Independent Projects: Create two separate React/TypeScript projects.
  • Expose Component: The remote application must expose a reusable React component (e.g., a simple button or card).
  • Consume Component: The host application must consume and render the exposed component from the remote application.
  • TypeScript Support: Both projects must be set up with TypeScript.
  • Webpack Configuration: Properly configure Webpack in both projects to enable Module Federation.
  • Dynamic Loading: The host should dynamically load the remote component.

Expected Behavior:

When the host application is run, it should fetch the necessary code from the remote application (without a page reload) and display the remote component on its page.

Edge Cases to Consider:

  • Network Latency: How would the application behave if the remote module takes time to load? (While not explicitly tested in this challenge, consider the implications).
  • Build Configuration: Ensuring correct public paths and remotes configuration in Webpack.

Examples

Example 1: Basic Component Exposure and Consumption

Remote Application (e.g., remote-app):

  • Exposed Component (src/components/Greeting.tsx):
    import React from 'react';
    
    interface GreetingProps {
        name: string;
    }
    
    const Greeting: React.FC<GreetingProps> = ({ name }) => {
        return <h1>Hello, {name} from Remote!</h1>;
    };
    
    export default Greeting;
    
  • Webpack Configuration (simplified):
    // webpack.config.js for remote-app
    const { ModuleFederationPlugin } = require('webpack').container;
    
    module.exports = {
        // ... other configurations
        plugins: [
            new ModuleFederationPlugin({
                name: "remoteApp",
                filename: "remoteEntry.js",
                exposes: {
                    "./Greeting": "./src/components/Greeting.tsx",
                },
            }),
        ],
    };
    

Host Application (e.g., host-app):

  • Component Consumption (src/App.tsx):
    import React, { Suspense, lazy } from 'react';
    const Greeting = lazy(() => import('remoteApp/Greeting'));
    
    function App() {
        return (
            <div>
                <h1>Host Application</h1>
                <Suspense fallback={<div>Loading...</div>}>
                    <Greeting name="World" />
                </Suspense>
            </div>
        );
    }
    
    export default App;
    
  • Webpack Configuration (simplified):
    // webpack.config.js for host-app
    const { ModuleFederationPlugin } = require('webpack').container;
    
    module.exports = {
        // ... other configurations
        plugins: [
            new ModuleFederationPlugin({
                name: "hostApp",
                remotes: {
                    remoteApp: "remoteApp@http://localhost:3002/remoteEntry.js", // Assuming remote is served on port 3002
                },
                shared: ["react", "react-dom"],
            }),
        ],
    };
    

Output (in the browser for the Host Application):

Host Application
Hello, World from Remote!

Explanation: The host-app dynamically imports the Greeting component exposed by remoteApp. When the Greeting component is rendered, it displays the message "Hello, World from Remote!".

Constraints

  • Development Environment: You are expected to set up this solution locally for development.
  • Port Configuration: Ensure that both applications can run on different ports (e.g., host on 3001, remote on 3002) and that the host can correctly resolve the remote entry point.
  • Dependency Management: Use standard package managers like npm or yarn.
  • Tooling: Webpack 5+ is required for Module Federation.

Notes

  • This challenge requires setting up two distinct projects. You can achieve this by creating two separate directories, each with its own package.json and Webpack configuration.
  • Consider using tools like create-react-app (with ejecting for Webpack customization) or Vite (with custom Webpack configuration) as a starting point, or manually configure Webpack.
  • The shared configuration in the ModuleFederationPlugin is crucial for ensuring that React and ReactDOM are not duplicated across your micro-frontends, which can lead to runtime errors.
  • Pay close attention to the name and filename options in the ModuleFederationPlugin for both the host and remote.
  • The remotes configuration in the host specifies how to find and load modules from other federated applications. The format typically looks like "remoteName@url".
  • Dynamic imports (lazy) and Suspense are the standard React patterns for handling asynchronous component loading.
Loading editor...
typescript