Create a module federation app

Developed by Jack Herrington, co-author of the "Practical Module Federation" book and technology-focused YouTuber.

create-mf-app is a tool that creates a Module Federation application, API server, or library based on one of multiple different templates.

Generating Application

This guide provides a comprehensive walkthrough on how to use the create-mf-app tool to create a Module Federation application and integrate it with Zephyr.

To execute npm package binaries, use the npx command as follows:

npx create-mf-app

Choose a name for your application. Ensure you designate one host and at least one remote, choosing names that reflect their roles accordingly.

? Pick the name of your app: (host)

Select the type of application:

? Project Type: (Use arrow keys)
 Application
  API Server
  Library

You may use the default port option:

? Port number: (8080)

We conducted tests using React, but other frameworks may also be compatible. See the Module Federation guide for more details on supported frameworks.

? Framework: (Use arrow keys)
lit-html
preact
 react
react-esm
solid-js
svelte
vanilla
(Move up and down to reveal more choices)

We prefer TypeScript, but Zephyr is compatible with both TypeScript and JavaScript.

? Language:
 typescript
javascript

Both CSS and Tailwind are viable options for styling:

? CSS: (Use arrow keys)
 CSS
Tailwind

While both packing options are viable, we strongly recommend Rspack for its performance. See benchmark of Rspack.

? Bundler: (Use arrow keys)
Webpack
 Rspack

Adding Zephyr to Your Module Federation App

Installing Zephyr plugin:

For Rspack:

cd your-app-name
npm install zephyr-rspack-plugin --save-dev

For Webpack:

cd your-app-name
npm install zephyr-webpack-plugin --save-dev

Zephyr Configuration Integration

Incorporate the Zephyr configuration into your existing setup by wrapping the current configuration object with Zephyr's functionality.

Rspack
Webpack

For Rspack, update your rspack.config.js:

rspack.config.js
const rspack = require('@rspack/core');
const refreshPlugin = require('@rspack/plugin-react-refresh');
const isDev = process.env.NODE_ENV === 'development';
const path = require('path');
const { withZephyr } = require('zephyr-rspack-plugin');

const printCompilationMessage = require('./compilation.config.js');

/**
 * @type {import('@rspack/cli').Configuration}
 */
module.exports = withZephyr()({
  context: __dirname,
  entry: {
    main: './src/index.ts',
  },

  devServer: {
    port: 8080,
    historyApiFallback: true,
    watchFiles: [path.resolve(__dirname, 'src')],
    onListening: function (devServer) {
      const port = devServer.server.address().port;
      printCompilationMessage('compiling', port);

      devServer.compiler.hooks.done.tap('OutputMessagePlugin', (stats) => {
        setImmediate(() => {
          if (stats.hasErrors()) {
            printCompilationMessage('failure', port);
          } else {
            printCompilationMessage('success', port);
          }
        });
      });
    },
  },

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

  module: {
    rules: [
      {
        test: /\.svg$/,
        type: 'asset',
      },
      {
        test: /\.scss$/,
        use: [
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: {
                  tailwindcss: {},
                  autoprefixer: {},
                },
              },
            },
          },
        ],
        type: 'css',
      },
      {
        test: /\.(jsx?|tsx?)$/,
        use: [
          {
            loader: 'builtin:swc-loader',
            options: {
              sourceMap: true,
              jsc: {
                parser: {
                  syntax: 'typescript',
                  tsx: true,
                },
                transform: {
                  react: {
                    runtime: 'automatic',
                    development: isDev,
                    refresh: isDev,
                  },
                },
              },
              env: {
                targets: [
                  'chrome >= 87',
                  'edge >= 88',
                  'firefox >= 78',
                  'safari >= 14',
                ],
              },
            },
          },
        ],
      },
    ],
  },

  plugins: [
    new rspack.container.ModuleFederationPlugin({
      name: 'create_mf_app_host',
      filename: 'remoteEntry.js',
      exposes: {},
      shared: {
        react: { eager: true },
        'react-dom': { eager: true },
        'react-router-dom': { eager: true },
      },
    }),
    new rspack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    }),
    new rspack.ProgressPlugin({}),
    new rspack.HtmlRspackPlugin({
      template: './src/index.html',
    }),
    isDev ? new refreshPlugin() : null,
  ].filter(Boolean),
});

Setting up Git Repository

Zephyr requires a Git repository to track your project. Follow these steps to set up your repository:

  1. Initialize the Git Repository:
git init
  1. Stage Files for Commit:
git add .
  1. Create an Initial Commit:
git commit -m "Initial commit with Zephyr"
  1. Set the Default Branch:
git branch -M main
  1. Create and Link Remote Repository:
GitHub CLI
Manual
gh repo create
  1. Push to Remote:
git push -u origin main

Build and Deploy

Build your application to deploy it with Zephyr:

npm run build

During the build process, Zephyr will:

  1. Extract your Module Federation configuration
  2. Upload your built assets to Zephyr Cloud
  3. Provide you with deployment URLs

Your application will be automatically available at the provided Zephyr URL. Each deployment creates a new version of your application with its own permanent URL.

Next Steps

For more insights on building micro-frontends with Module Federation, check out Jack Herrington's video tutorial.