React: Micro Frontend using Webpack 5 Module Federation

selllami

Yassine Sellami

Posted on October 2, 2022

React: Micro Frontend using Webpack 5 Module Federation

In progress !!!!!!!!

Init projects

mkdir /opt/mfe
cd /opt/mfe

# create dashboard app
npx create-react-app dashboard-app --template typescript
# create profile app
npx create-react-app profile-app --template typescript
# create host app
npx create-react-app container --template typescript
Enter fullscreen mode Exit fullscreen mode

Install dependencies

Add mui

npm install @mui/material @emotion/react @emotion/styled
npm install @fontsource/roboto
npm install @mui/icons-material
Enter fullscreen mode Exit fullscreen mode

Add MFE dep

npm install --save-dev webpack webpack-cli webpack-merge 
html-webpack-plugin webpack-dev-server 

npm install --save-dev @babel/core @babel/eslint-parser @babel/plugin-transform-runtime @babel/preset-env @babel/preset-react  @babel/preset-typescript @babel/runtime babel-loader css-loader postcss postcss-loader style-loader
Enter fullscreen mode Exit fullscreen mode

Config

  • Replace index.tsx with bootstrap.tsx:
  • create file ./src/index.ts:
import { createBrowserHistory, Update } from 'history';

import('./bootstrap').then(({ mount }) => {
  const localRoot = document.getElementById('dashboardApp-local');
  const browserHistory = createBrowserHistory();

  mount({
    mountPoint: localRoot!,
    historyStrategy: browserHistory,
  });
});

Enter fullscreen mode Exit fullscreen mode

Add file .babelrc:

vi .babelrc
----
{
  "presets": [
    "@babel/preset-typescript",
    "@babel/preset-react",
    "@babel/preset-env"
  ],
  "plugins": [["@babel/transform-runtime"]]
}
Enter fullscreen mode Exit fullscreen mode

Add file webpack.config.js:

vi webpack.config.js
----
const HtmlWebPackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

const deps = require('./package.json').dependencies;
module.exports = {
  output: {
    publicPath: 'http://localhost:8081/',
  },

  resolve: {
    extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
  },

  devServer: {
    port: 8081,
    historyApiFallback: true,
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },

  module: {
    rules: [
      {
        test: /\.m?js/,
        type: 'javascript/auto',
        resolve: {
          fullySpecified: false,
        },
      },
      {
        test: /\.(css|s[ac]ss)$/i,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
      },
      {
        test: /\.(ts|tsx|js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
    ],
  },

  plugins: [
    new ModuleFederationPlugin({
      name: 'dashboard-app',
      filename: 'remoteEntry.js',
      remotes: {},
      exposes: {
        './DashboardAppIndex': './src/bootstrap',
      },
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        'react-dom': {
          singleton: true,
          requiredVersion: deps['react-dom'],
        },
      },
    }),
    new HtmlWebPackPlugin({
      template: './src/index.html',
    }),
  ],
};

Enter fullscreen mode Exit fullscreen mode
  • Replace run script:
"scripts": {
    "build": "webpack --mode production",
    "build:dev": "webpack --mode development",
    "build:start": "cd dist && PORT=8082 npx serve",
    "start": "webpack serve --open --mode development",
    "start:live": "webpack serve --open --mode development --live-reload --hot"
  },
Enter fullscreen mode Exit fullscreen mode
  • Webpack config for container:
const HtmlWebPackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

const deps = require('./package.json').dependencies;
module.exports = {
  output: {
    publicPath: 'http://localhost:8080/',
  },

  resolve: {
    extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
  },

  devServer: {
    port: 8080,
    historyApiFallback: true,
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },

  module: {
    rules: [
      {
        test: /\.m?js/,
        type: 'javascript/auto',
        resolve: {
          fullySpecified: false,
        },
      },
      {
        test: /\.(css|s[ac]ss)$/i,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
      },
      {
        test: /\.(ts|tsx|js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
    ],
  },

  plugins: [
    new ModuleFederationPlugin({
      name: 'container',
      filename: 'remoteEntry.js',
      remotes: {
        dashboardApp: 'dashboardApp@http://localhost:8081/remoteEntry.js',
        profileApp: 'profileApp@http://localhost:8082/remoteEntry.js',
      },
      exposes: {},
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        'react-dom': {
          singleton: true,
          requiredVersion: deps['react-dom'],
        },
      },
    }),
    new HtmlWebPackPlugin({
      template: './src/index.html',
    }),
  ],
};

Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
selllami
Yassine Sellami

Posted on October 2, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related