Microfrontends are transforming how modern web applications are built, enabling teams to develop and deploy independent features without impacting the entire application. In this article, we’ll explore how to set up microfrontends using Nx, a powerful monorepo tool, with detailed examples and explanations. By the end, you’ll have a clear understanding of how to leverage Nx for a scalable microfrontend architecture.

Why Use Nx for Microfrontends?

Nx simplifies managing multiple projects in a monorepo, making it an excellent choice for microfrontend architecture. Key benefits include:
  • Integrated Development Tools: Built-in support for modern frameworks like React, Angular, and Next.js.
  • Module Federation Support: Enables dynamic loading of microfrontends.
  • Code Sharing: Easily share libraries across microfrontends.
  • Scalability: Manage multiple teams working on different features seamlessly.

Setting Up Microfrontends with Nx

Step 1: Initialize an Nx Monorepo Start by creating a new Nx workspace:
npx create-nx-workspace@latest
  • Choose Integrated Monorepo.
  • Name your workspace (e.g., microfrontend-demo).
  • Select React as the primary framework.
  • Step 2: Add Host and Remote Applications a) Create the Host Application (Shell)
    The host application serves as the central point for loading and managing microfrontends.
nx g @nrwl/react:application shell
b) Create Remote Applications (Microfrontends)
Each microfrontend is created as an independent application:
nx g @nrwl/react:application dashboard

nx g @nrwl/react:application profile
Step 3: Configure Module Federation Nx supports Module Federation out of the box, making it easy to connect host and remote applications. a) Configure the Host (Shell) Edit apps/shell/webpack.config.js:
const { withModuleFederation } = require('@nrwl/react/module-federation');
module.exports = withModuleFederation({

  name: 'shell',

  remotes: ['dashboard', 'profile'],

});
b) Configure the Remotes For each microfrontend, configure webpack.config.js: Dashboard (apps/dashboard/webpack.config.js):
const { withModuleFederation } = require('@nrwl/react/module-federation');
module.exports = withModuleFederation({

  name: 'dashboard',

  exposes: {

    './Module': './src/app/remote-entry',

  },

});
Profile (apps/profile/webpack.config.js):
const { withModuleFederation } = require('@nrwl/react/module-federation');
module.exports = withModuleFederation({

  name: 'profile',

  exposes: {

    './Module': './src/app/remote-entry',

  },

});
Step 4: Load Microfrontends in the Host In the host application, dynamically load the microfrontends using React’s React.lazy: App.js (Host):
import React, { Suspense } from 'react';

import { BrowserRouter, Route, Routes } from 'react-router-dom';
const Dashboard = React.lazy(() => import('dashboard/Module'));

const Profile = React.lazy(() => import('profile/Module'));
function App() {

  return (

    <BrowserRouter>

      <Suspense fallback={

<div>Loading...</div>
}>

        <Routes>

          <Route path="/dashboard" element={<Dashboard />} />

          <Route path="/profile" element={<Profile />} />

        </Routes>

      </Suspense>

    </BrowserRouter>

  );

}
export default App;
Step 5: Run the Applications Start all applications simultaneously:
nx serve shell

nx serve dashboard

nx serve profile
The host application (shell) dynamically loads the microfrontends (dashboard and profile).

Addressing Routing in Microfrontends

In microfrontend architectures, routing can become complex due to the independence of each application. Here’s how to manage it effectively:
  • Host Manages Global Routing: The host application handles main routes and decides which microfrontend to load.
  • Microfrontends Manage Local Routes: Each remote manages its internal routes to avoid conflicts with others.
  • For example, in the host, a route might map to a microfrontend:
<Route path="/dashboard" element={<Dashboard />} />
Within the dashboard microfrontend, you can have local routing:
import { BrowserRouter, Route, Routes } from 'react-router-dom';
function DashboardApp() {

  return (

    <Routes>

      <Route path="/" element={<DashboardHome />} />

      <Route path="/settings" element={<DashboardSettings />} />

    </Routes>

  );

}
export default DashboardApp;

Diagram of Microfrontend Architecture

Below is a visual representation of how the host and microfrontends interact:
Monorepo:

|

├── apps/

|   ├── shell (host)

|   ├── dashboard (remote)

|   └── profile (remote)

|

├── libs/ (optional shared libraries)

└── nx.json (monorepo configuration)

Key Notes

  1. Host dynamically loads microfrontends using Module Federation.
  2. Microfrontends are independent, allowing teams to work in isolation.
  3. Shared Libraries can be placed in the libs folder to reduce duplication.

Final Thoughts

By leveraging Nx for microfrontend architecture, you gain a scalable, modular, and efficient development environment. With features like built-in Module Federation support, code-sharing capabilities, and seamless integration with modern frameworks, Nx is a powerful tool for modern web development.

Start your journey with Nx today and revolutionize your approach to building scalable web applications!