Microfrontends Made Easy: A Practical Guide for React Developers
by Ibimina H.
If you think managing complex frontend applications is a daunting task, you’re not alone. Many developers are finding monolithic architectures inadequate for today’s fast-paced development landscape, struggling with issues of scalability and team collaboration. Microfrontends offer a game-changing solution by breaking down applications into independent modules that can be developed and deployed without interference. In this article, we’ll explore how to implement microfrontends using React and modern tools like Webpack Module Federation, equipping you with the knowledge to enhance your projects’ scalability and maintainability.
What Are Microfrontends?
Microfrontends extend the principles of microservices to the frontend, enabling teams to work on different features independently. These individual frontend modules can be built using different frameworks, deployed separately, and seamlessly integrated into a single user interface.
Key Benefits of Microfrontends
Implementing Microfrontends with React and Webpack Module Federation
One of the most popular tools for implementing microfrontends is Webpack Module Federation. It allows you to dynamically load and share code between multiple applications at runtime. Let’s walk through a practical example.
Step 1: Setting Up the Project Structure
We’ll create two React applications:
Host Application: The main application that integrates multiple microfrontends.
Remote Application: A microfrontend that provides a specific feature (e.g., a dashboard).
Install Dependencies
npm install webpack webpack-cli webpack-dev-server html-webpack-plugin webpack-merge babel-loader --save-dev
npm install react react-dom
First, ensure you have Webpack and React installed in both applications:
Step 2: Configuring Webpack Module Federation
Remote Application Configuration
In the remote application, configure Webpack to expose a component (e.g., a Dashboard):
const { ModuleFederationPlugin } = require("webpack").container;
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = (_, argv) => ({
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "build"),
publicPath: argv.mode === 'development' ? `https://localhost:3001/` : 'replace with live url for remote application',
},
devServer: {
port: 3001,
},
resolve: {
extensions: [".jsx", ".js", ".json"],
},
module: {
rules: [
{
test: /\.(js|jsx)$/, // or /\.(ts|tsx)$/ if using TypeScript
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
},
},
},
{
test: /\.svg$/,
oneOf: [
{
resourceQuery: /component/, // Use "?component" in import to load as React component
use: ["@svgr/webpack"],
},
{
use: {
loader: "file-loader",
options: {
name: "[name].[hash].[ext]",
outputPath: "assets", // Save inside "dist/assets/"
},
},
},
],
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
plugins: [
new ModuleFederationPlugin({
name: "remote",
filename: "remoteEntry.js",
exposes: {
"./App": "./src/App", // Exposing the Microfrontend's App component
},
shared: {
react: { singleton: true, eager: true },
'react-dom': { singleton: true, eager: true },
},
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
],
});
Host Application Configuration
In the host application, configure Webpack to consume the remote module:
const { ModuleFederationPlugin } = require("webpack").container;
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = (_, argv) => ({
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "build"),
publicPath:
argv.mode === "development"
? `https://localhost:3000/`
: "replace with live url for host application",
},
devServer: {
port: 3000,
},
resolve: {
extensions: [".jsx", ".js", ".json"],
},
module: {
rules: [
{
test: /\.(js|jsx)$/, // or /\.(ts|tsx)$/ if using TypeScript
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
},
},
},
{
test: /\.svg$/,
oneOf: [
{
resourceQuery: /component/, // Use "?component" in import to load as React component
use: ["@svgr/webpack"],
},
{
use: {
loader: "file-loader",
options: {
name: "[name].[hash].[ext]",
outputPath: "assets", // Save inside "dist/assets/"
},
},
},
],
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
plugins: [
new ModuleFederationPlugin({
name: "host",
filename: "remoteEntry.js",
remotes: {
remote: `remote@replace with live url or local host for remote application/remoteEntry.js`,
},
shared: {
react: { singleton: true, eager: true },
"react-dom": { singleton: true, eager: true },
},
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
],
});
Update Script
Replace start and build script in both remote and host application package.json
"start": "webpack serve --config webpack.config.js --mode development",
"build": "webpack --config webpack.config.js --mode production",
Step 3: Using the Remote Microfrontend in the Host Application
In the host application, dynamically import the remote module using React’s lazy and Suspense:
import './App.css';
import React, { Suspense } from "react";
// Importing the Microfrontend dynamically
const App = React.lazy(() => import("remote/App"));
function AppPage() {
return (
<div>
<h1>Host Application</h1>
<Suspense fallback={<div>Loading Microfrontend...</div>}>
<App />
</Suspense>
</div>
);
}
export default AppPage;
Step 4: Running the Applications
Start both applications:
cd remote && npm start
cd host && npm start
Step 5: Deploy the Applications to GitHub
Push Your Applications to GitHub Initialize a git repository
git init
?Commit and push to Github
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/your-username/your-repo-name.git
git push -u origin main
Step 6: Deploy to Vercel
Sign Up/Log In to Vercel:
Go to Vercel and sign up or log in using your GitHub account.
Create a New Project:
Click on New Project.
Select the GitHub repository where your microfrontend code is hosted.
Configure the Build Settings:
Vercel will automatically detect your React application and configure the build settings.
For the remote application, repeat the same steps in a separate Vercel project.
Deploy:
Click Deploy to start the deployment process.
Vercel will build and deploy your application, providing you with a live URL.
Step 7: Update Remote Application URL
After deploying the remote application, update the remoteEntry.js URL in the host application:
Get the Remote Application URL:
Once the remote application is deployed, Vercel will provide a live URL (e.g., https://remote-app.vercel.app).
Update Webpack Configuration:
In the host application, update the remotes configuration in webpack.config.js:
remotes: {
??remote: "remote@https://remote-app.vercel.app/remoteEntry.js",
},
Redeploy the Host Application:
Push the changes to GitHub, and Vercel will automatically redeploy the host application.
Conclusion
Microfrontends represent a transformative shift in frontend development, allowing teams to operate with greater autonomy and agility. The integration of Webpack Module Federation has proven that incorporating a React-based microfrontend into existing applications is not just feasible but also advantageous. As we witness the rising trend of microfrontends, we can expect continuous improvements from tools like Webpack 5 and frameworks such as Single-Spa, which will further streamline the development process. By adopting this modular architecture, developers are empowered to create applications that are not only scalable but also easier to maintain and adapt over time. Whether you’re working on a large-scale enterprise application or a modular project, microfrontends provide a pathway to efficient and collaborative development.