DEV Community

Cover image for Rspack with Module federation V2 is the future
Ibrahim Shamma
Ibrahim Shamma

Posted on

Rspack with Module federation V2 is the future

The module federation team cared deeply about the DevX in their latest release.
The purpose of this blog is to go through setting up the project, and explain a little on each decision being taken here.

Github Repo:

Wanna see the full code, check this app out:
https://github.com/IbrahimShamma99/microfrontend-rspack-module-federation-v2

Tech Stack

pnpm

This package manager recently became my favorite, for many reasons, including how to handles non flattened node_modules which helps in resolving security vulnerabilities effectively.
Another thing amazing about pnpm which how it simply you create monorepos, without the need to read much of docs like other solutions.

For more info about this amazing package manager I highly recommend this article published by refine.dev

React

Very obvious choice, though I will add vue into the mix soon.

Typescript

Consumer apps in MF V2 generates types out of remotes and being stored by default into @mf-types directory, we are using TS to demonstrate that.

Rspack

In previous blogs we were using webpack, but ever since rspack hit the V1 mark I have been migrating every app I work with into rspack, why? same webpack configuration but with faster and out of the box support for code splitting and module federation, what could you ask for more?

some members webpack core team are the ones building rspack which they have the experience and architecture knowledge transfered into the new bunder rspack should not be looked at as a V1 package, instead it is a continuation to what webpack is.

Setup project

Initialize dir:

First we are going to create the directory and setup package.json.

mkdir ./out-project-name
cd ./out-project-name

pnpm init
Enter fullscreen mode Exit fullscreen mode

Add ESM Config:

We want to make sure our package is ESM based, not CommonJS, so we do the following modification to package.json

npm pkg set type="module"
Enter fullscreen mode Exit fullscreen mode

For more info check this out

Setup Engine and Package manager:

Simply run the following two commands

npm pkg set engines.node=">=22.10.0"
npm pkg set packageManager="pnpm@9.15.1"
Enter fullscreen mode Exit fullscreen mode

Add pnpm worksapce

Run this touch command:

touch pnpm-workspace.yaml
Enter fullscreen mode Exit fullscreen mode

it will create pnpm-workspace.yaml config file which we will add to it the following yaml config:

packages:
  - "apps/*"
  - "packages/*"
Enter fullscreen mode Exit fullscreen mode

In respect to each will mean:

  • apps: Consumed and Consumer apps which run on specific port.
  • packages: shared slices of code between different apps, it could be UI libraries or communication with backends etc.

Create apps

mkdir apps
cd ./apps

# lets create two react typescript app one named header and other named main
pnpm create rsbuild@latest

cd ../
Enter fullscreen mode Exit fullscreen mode

Install module federation:

Command:

pnpm add @module-federation/enhanced --workspace-root
pnpm add @module-federation/rsbuild-plugin --save-dev --workspace-root
Enter fullscreen mode Exit fullscreen mode

We are adding --workspace-root flag stating that this package is a global package that can be shared between apps, this is helpful for having single place to manage global dependency versioning

Setup Consumed app

going into apps/header/rsbuild.config.ts
we would find rsbuild configuration

rsbuild is the build tool for rspack.

Let's add the following code:

// src/Header.tsx

export default function Header() {
  return (
    <div>
      <h1>Main Header</h1>
    </div>
  );
}

// apps/header/rsbuild.config.ts
import { defineConfig } from "@rsbuild/core";
import { pluginReact } from "@rsbuild/plugin-react";
import { pluginModuleFederation } from "@module-federation/rsbuild-plugin";

export default defineConfig({
  server: {
    port: 3001,
  },
  plugins: [
    pluginReact(),

    pluginModuleFederation({
      name: "header",
      exposes: {
        "./header": "./src/Header.tsx",
      },
      shared: ["react", "react-dom"],
    }),
  ],
});
Enter fullscreen mode Exit fullscreen mode

Explanation:

It is much like the configuration we explained before here

Configuration is pretty much similar to previous MF versions.

Setup Consumer app:

// src/FallbackHeader.tsx
export function FallbackHeader() {
  return (
    <div>
      <h1>Fallback Header</h1>
    </div>
  );
}

// src/App.tsx

import { FallbackHeader } from "./FallbackHeader";
import { Suspense } from "react";
import Header from "header/header";

const App = () => {
  return (
    <div className="content">
      <Suspense fallback={<FallbackHeader />}>
        <Header />
      </Suspense>
      <h1>Main App</h1>
    </div>
  );
};

export default App;


Enter fullscreen mode Exit fullscreen mode

Want to learn more about why we adding a fallback Header with the suspense? please checkout part 5 & 6 of this series:
Part 5
Part 6

running the app:

One of the true powers of pnpm is the filter flag
which let root package.json exposes internal monorepo scripts

ROOT/package.json
{
"scripts": {
    "header": "pnpm --filter ./apps/header",
    "main": "pnpm --filter ./apps/main"
  },
}
Enter fullscreen mode Exit fullscreen mode

and going into root app and run the following commands each on separate terminal:

pnpm header dev
pnpm main dev
Enter fullscreen mode Exit fullscreen mode

And your app is running!

Image description

Top comments (0)