Setting up React Micro Frontends with Vite Module Federation using @originjs/vite-plugin-federation

Setting up React Micro Frontends with Vite Module Federation using @originjs/vite-plugin-federation

Introduction

Webpack 5's powerful new feature, Module Federation, makes it possible for independent applications to share and dynamically load code from one another. In the React ecosystem, Vite is becoming more and more popular due to its modern, lightning-fast build tools and development servers. In this blog article, we will look at how to use Vite and React along with the @originjs/vite-plugin-federation to construct scalable, adaptable, and high-performing micro-frontend apps.

What is Module Federation?

Module Federation is a feature that allows multiple independent applications to share code dynamically at runtime. It enables developers to build and deploy applications as smaller, more focused pieces that can be consumed by other applications, increasing code reuse and decreasing duplication.

Why Use Vite?

Vite is a next-generation build tool and development server that leverages the native ES modules feature in modern browsers. It provides a faster development experience, with features like hot module replacement and optimized production builds. Vite also has first-class support for popular frameworks like React, Vue, and Svelte, making it a great choice for modern web development.

Getting Started

To demonstrate Module Federation with Vite and React, we will create two separate applications: a host application and a remote application. The remote application will export a React component that the host application will consume.

  1. Setting up the Remote Application

Start by creating a new Vite project using the React template:

npm init vite@latest remote-app -- --template react
cd remote-app
npm install
// package.json
{
  "name": "remote-app",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite --port=5001 --strictPort",
    "build": "vite build",
    "preview": "vite preview --port=5001 --strictPort",
    "deploy": "npm run build && npm run preview"
  },
  "dependencies": {
    "@originjs/vite-plugin-federation": "^1.2.2",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "@vitejs/plugin-react": "^3.1.0",
    "vite": "^4.2.0"
  }
}

Now, let's create a new RemoteComponent that will be shared by the host application:

// src/RemoteComponent.js
import React from 'react';

const RemoteComponent = () => (
  <div>
    <h2>Hello from Remote Component!</h2>
  </div>
);

export default RemoteComponent;
  1. Configuring Module Federation

To use Module Federation with Vite, we need to install the @originjs/vite-plugin-federation plugin:

npm install @originjs/vite-plugin-federation

Next, update the vite.config.js file to include the plugin and configure the remote component:

// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import federation from '@originjs/vite-plugin-federation';

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'remoteApp',
      filename: 'remoteEntry.js',
      exposes: {
        './RemoteComponent': './src/RemoteComponent',
      },
      shared: ['react', 'react-dom'],
    }),
  ],

  build: {
    modulePreload: false,
    target: 'esnext',
    minify: false,
    cssCodeSplit: false,
  },
});
  1. Building and Previewing the Remote Application

Before we can see the changes in the remote application, we need to build and preview it:

npm run deploy

The remote application will be running on port 5001.

  1. Setting up the Host Application

Now, let's create the host application that will consume the RemoteComponent:

npm init vite@latest host-app -- --template react
cd host-app
npm install
// package.json
{
  "name": "host-app",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite --port=5500 --strictPort",
    "build": "vite build",
    "preview": "vite preview --port=5500 --strictPort"
  },
  "dependencies": {
    "@originjs/vite-plugin-federation": "^1.2.2",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "vite-plugin-federation": "^0.0.1"
  },
  "devDependencies": {
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "@vitejs/plugin-react": "^3.1.0",
    "vite": "^4.2.0"
  }
}
  1. Configuring the Host Application

Install the @originjs/vite-plugin-federation plugin in the host application:

npm install @originjs/vite-plugin-federation

Then, update the vite.config.js file to include the plugin and configure the remote application:

// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import federation from '@originjs/vite-plugin-federation';

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'hostApp',
      remotes: {
        remoteApp: 'http://localhost:5001/assets/remoteEntry.js',
      },
      shared: ['react', 'react-dom'],
    }),
  ],
});
  1. Consuming the Remote Component

In the host application, update src/App.jsx to import and render the RemoteComponent from the remote application:

// src/App.jsx
import React, { lazy, Suspense } from 'react';
import './App.css';

const RemoteComponent = lazy(() => import('remoteApp/RemoteComponent'));

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <h1>Host Application</h1>
        <Suspense fallback={<div>Loading Remote Component...</div>}>
          <RemoteComponent />
        </Suspense>
      </header>
    </div>
  );
}

export default App;
  1. Running the Host Application

With the remote application running on port 5001, start the host application:

// In the host-app directory
npm run dev

Now, open http://localhost:5500 in your browser, and you should see the host application consuming the RemoteComponent from the remote application.

Pros and Cons

Pros of Micro Frontends:

  1. Independent development: Micro frontends allow teams to work on separate parts of an application independently, reducing dependencies and improving development velocity.

  2. Easier scaling: As teams and applications grow, micro frontends can help distribute work across multiple teams, enabling easier scaling of development efforts.

  3. Incremental upgrades: Micro frontends enable teams to upgrade or change parts of an application independently, allowing for smoother transitions and reducing the risk of breaking changes.

  4. Better separation of concerns: Micro frontends promote a clear separation of concerns by organizing code into smaller, focused modules.

  5. Reusable components: Micro frontends can increase code reuse, as components can be shared across different parts of the application or even across different applications.

  6. Technology flexibility: Teams can choose different technology stacks for different parts of the application, making it easier to adopt new technologies or frameworks when appropriate.

Cons of Micro Frontends:

  1. Increased complexity: Introducing micro frontends can increase the overall complexity of the application, as developers need to manage communication between different parts of the app and handle possible inconsistencies.

  2. Potential performance issues: Loading multiple micro frontends can lead to performance issues if not implemented correctly, such as increased bundle sizes or slow loading times.

  3. Coordination overhead: While micro frontends enable independent development, coordinating the overall application's look and feel and ensuring consistency can become more challenging.

  4. Learning curve: Adopting micro frontends can have a learning curve, as developers need to understand the architecture and the specific tools and technologies used.

When to use Module Federation?

Module Federation is particularly useful in the following scenarios:

  1. Large-scale applications: When building a large application with multiple teams working on different parts, Module Federation can help manage dependencies and enable independent development and deployment.

  2. Distributed development: If different teams or organizations develop separate applications that need to share components or functionality, Module Federation can provide a flexible way to share and consume code.

  3. Evolving architecture: For applications that need to evolve or migrate from monolithic to microservices-based or micro frontend architectures, Module Federation can be a valuable tool to incrementally split and refactor the codebase.

  4. Applications with multiple versions: If you need to maintain and deploy different versions of an application with shared components, Module Federation can help manage these dependencies and reduce duplication.

Github Repo

https://github.com/adeeshsharma/react-vite-mod-fed

Conclusion

In this blog post, we demonstrated how to use Module Federation with Vite and React using @originjs/vite-plugin-federation to create scalable and flexible micro frontend applications. With this powerful combination, we can create modular applications that share code and dependencies at runtime, improving maintainability and performance.

By leveraging the benefits of Vite and React, along with Module Federation, developers can build high-performing, modern web applications with ease. This approach helps reduce duplication, enables easier updates, and fosters collaboration among teams working on independent applications.

Did you find this article valuable?

Support Adeesh's Software Engineering Insights by becoming a sponsor. Any amount is appreciated!